@@ -289,9 +310,9 @@ the memory from $800 to $1FFF can be added to the heap by calling
ProDOS 8 requires for every open file a page-aligned 1 KB I/O buffer. By default
these buffers are allocated by the cc65 runtime system on the heap using
-. While this is
+generally the best solution it means quite some overhead for (especially rather
+small) cc65 programs which do open files but don't make use of the heap otherwise.
The apple2 package comes with the alternative ProDOS 8 I/O buffer allocation
module Apple ][ specific functions
-The functions listed below are special for the Apple ][. See
-the for declaration and
+The functions and variables listed below are special for the Apple ][.
+See the for declaration and
usage.
- _auxtype
- _dos_type
- _filetype
+
- _datetime
+
- allow_lowercase
+
- beep
+
- dir_entry_count
+
- get_tv
- get_ostype
+
- gmtime_dt
+
- mktime_dt
- rebootafterexit
- ser_apple2_slot
- tgi_apple2_mix
+
- videomode
+
+
+
+Apple IIgs specific functions in accelerator.h
+
+In addition to those, the for declaration and
+usage.
+
+
+- detect_iigs
+
- get_iigs_speed
+
- set_iigs_speed
@@ -364,6 +407,10 @@ The names in the parentheses denote the symbols to be used for static linking of
with .
+ProDOS date/time manipulation
+
+
+The readdir and stat function return ProDOS timestamps in their file
+creation/modification time attributes. You can convert them to more portable
+time representations using either:
+
+
+
+
DIO
@@ -630,6 +704,16 @@ url="ca65.html" name="assembler manual">.
that can be used to set these variables. It is included in
+
+ extern struct datetime _datetime;
+
+
+
Example
A text file cannot be created with just the
diff --git a/doc/apple2enh.sgml b/doc/apple2enh.sgml
index 11d4feb7e..094ddd93e 100644
--- a/doc/apple2enh.sgml
+++ b/doc/apple2enh.sgml
@@ -63,7 +63,7 @@ Special locations:
While running . While this is
+generally the best solution it means quite some overhead for (especially rather
+small) cc65 programs which do open files but don't make use of the heap otherwise.
The apple2enh package comes with the alternative ProDOS 8 I/O buffer allocation
module Enhanced Apple //e specific functions
-The functions listed below are special for the enhanced Apple //e. See
-the for declaration and
+The functions and variables listed below are special for the Apple ][.
+See the for declaration and
usage.
- _auxtype
- _dos_type
- _filetype
+
- _datetime
+
- beep
+
- dir_entry_count
+
- get_tv
- get_ostype
+
- gmtime_dt
+
- mktime_dt
- rebootafterexit
- ser_apple2_slot
- tgi_apple2_mix
@@ -339,6 +345,20 @@ usage.
+Apple IIgs specific functions in accelerator.h
+
+In addition to those, the for declaration and
+usage.
+
+
+- detect_iigs
+
- get_iigs_speed
+
- set_iigs_speed
+
+
+
Hardware access
There's currently no support for direct hardware access. This does not mean
@@ -435,10 +455,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 .
+ProDOS date/time manipulation
+
+
+The readdir and stat function return ProDOS timestamps in their file
+creation/modification time attributes. You can convert them to more portable
+time representations using either:
+
+
+
+
DIO
@@ -619,7 +666,7 @@ url="ca65.html" name="assembler manual">.
auxiliary type. Therefore, some additional mechanism for specifying
the file types is needed.
- Specifying the File Type and Auxiliary Type
+ Specifying the File Type, Auxiliary Type and creation date
There are two global variables provided that allow the file type
and auxiliary type to be specified before a call to .
that can be used to set these variables. It is included in
+
+ extern struct datetime _datetime;
+
+
+
Example
A text file cannot be created with just the
diff --git a/doc/atari.sgml b/doc/atari.sgml
index 3057cd8a6..060bc8ad4 100644
--- a/doc/atari.sgml
+++ b/doc/atari.sgml
@@ -412,8 +412,9 @@ Please mind that ANTIC has memory alignment requirements for "player
missile graphics"-data, font data, display lists and screen memory. Creation
of a special linker configuration with appropriate aligned segments and
switching to that segment in the c-code is usually necessary. A more memory
-hungry solution consists in using the " function in conjunction with copying your data to the
+allocated memory.
Character mapping
diff --git a/doc/atmos.sgml b/doc/atmos.sgml
index cef7770e4..e330d9517 100644
--- a/doc/atmos.sgml
+++ b/doc/atmos.sgml
@@ -1,15 +1,15 @@
-Oric Atmos-specific information for cc65
+Tangerine Oric Atmos-specific information for cc65
,
-,
+,
-An overview over the Atmos runtime system as it is implemented for the cc65 C
-compiler.
+An overview over the Oric Atmos runtime system as it is implemented for the cc65
+C compiler. This target is not Oric-1 compatible.
diff --git a/doc/ca65.sgml b/doc/ca65.sgml
index 5789a7b42..79c8a8ca6 100644
--- a/doc/ca65.sgml
+++ b/doc/ca65.sgml
@@ -833,49 +833,40 @@ names like "Loop". Here is an example:
bne @Loop ; ERROR: Unknown identifier!
+
Unnamed labels
-If you really want to write messy code, there are also unnamed labels. These
-labels do not have a name (you guessed that already, didn't you?). A colon is
-used to mark the absence of the name.
+If you really want to write messy code, there are also unnamed labels. To define
+an unnamed label, use sole : .
-Unnamed labels may be accessed by using the colon plus several minus or plus
-characters as a label designator. Using the '-' characters will create a back
-reference (use the n'th label backwards), using '+' will create a forward
-reference (use the n'th label in forward direction). An example will help to
-understand this:
+To reference an unnamed label, use : with several - or + characters.
+The - characters will create a back reference (n'th label backwards),
+the + will create a forward reference (n'th label in forward direction).
+As an alternative, angle brackets < and > may be used
+instead of - and + with the same meaning.
+
+Example:
- : lda (ptr1),y ; #1
- cmp (ptr2),y
- bne :+ ; -> #2
- tax
- beq :+++ ; -> #4
- iny
- bne :- ; -> #1
- inc ptr1+1
- inc ptr2+1
- bne :- ; -> #1
-
- : bcs :+ ; #2 -> #3
- ldx #$FF
- rts
-
- : ldx #$01 ; #3
- : rts ; #4
+ cpy #0
+ beq :++
+ :
+ sta $2007
+ dey
+ bne :-
+ :
+ rts
-As you can see from the example, unnamed labels will make even short
-sections of code hard to understand, because you have to count labels
-to find branch targets (this is the reason why I for my part do
-prefer the "cheap" local labels). Nevertheless, unnamed labels are
-convenient in some situations, so it's your decision.
+Unnamed labels may make even short sections of code hard to understand, because
+you have to count labels to find branch targets. It's better to prefer the
+"cheap" local labels. Nevertheless, unnamed labels are convenient in some
+situations, so it's up to your discretion.
organize named symbols, not
unnamed ones, so scopes don't have an effect on unnamed labels.
-
Using macros to define labels and constants
While there are drawbacks with this approach, it may be handy in a few rare
@@ -1070,7 +1061,7 @@ The namespace token (
The only way to deny access to a scope from the outside is to declare a scope
@@ -2871,6 +2862,26 @@ See: ,
+
+ Switch on or off line continuations using the backslash character
+ before a newline. The option is off by default.
+ Note: Line continuations do not work in a comment. A backslash at the
+ end of a comment is treated as part of the comment and does not trigger
+ line continuation.
+
+ Example:
+
+
+ .feature line_continuations + ; Allow line continuations
+
+ lda \
+ #$20 ; This is legal now
+
+
+ For backward compatibility reasons, the .LINECONT + control command
+ is also supported and enables the same feature.
+
long_jsr_jmp_rts
Affects 65816 mode only.
@@ -3375,26 +3386,6 @@ See: ,
-
- Switch on or off line continuations using the backslash character
- before a newline. The option is off by default.
- Note: Line continuations do not work in a comment. A backslash at the
- end of a comment is treated as part of the comment and does not trigger
- line continuation.
- The command can be followed by a '+' or '-' character to switch the
- option on or off respectively.
-
- Example:
-
-
- .linecont + ; Allow line continuations
-
- lda \
- #$20 ; This is legal now
-
-
-
.LIST
Enable output to the listing. The command can be followed by a boolean
@@ -3908,7 +3899,7 @@ See: , ,.TAG
- Allocate space for a struct or union.
+ Allocate space for a struct or union. This is equivalent to
+ with the
+ of a struct.
Example:
@@ -4104,6 +4097,7 @@ See: ,[
+ See: ][
].UNDEF, .UNDEFINE
@@ -4493,9 +4487,9 @@ different:
- Macros defined with
may not
span more than a line. You may use line continuation (see ) to spread the definition over
- more than one line for increased readability, but the macro itself
- may not contain an end-of-line token.
+ id="line_continuations" name="line_continuations">
) to spread the
+ definition over more than one line for increased readability, but the
+ macro itself may not contain an end-of-line token.
- Macros defined with
share
the name space with classic macros, but they are detected and replaced
@@ -4869,10 +4863,15 @@ compiler, depending on the target system selected:
Structs and unions are special forms of [. They
are, to some degree, comparable to their C counterparts. Both have a list of
-members. Each member allocates storage, and optionally may have a name whose
-value, in the case of a struct, usually is the storage offset from the
-beginning, and in the case of a union, doesn't change, and usually is zero.
+members. Each member allocates storage, and optionally may have a name.
+Each named member has a constant value equal to the storage offset from the
+beginning of the structure. In the case of a union, all members are placed at
+the same offset, typically 0.
+
+Each named member also has a storage size which can be accessed with the
+] operator. The struct or union itself
+also has a Declaration
@@ -4899,8 +4898,9 @@ A struct or union may not necessarily have a name. If it is anonymous, no
local scope is opened; the identifiers used to name the members are placed
into the current scope instead.
-A struct may contain unnamed members and definitions of local structs/unions.
-The storage allocators may contain a multiplier, as in the example below:
+Storage allocators may contain a multiplier. A struct may also contain members
+and definitions of local structs/unions. Example:
+
.struct Circle
.struct Point
@@ -4909,7 +4909,8 @@ The storage allocators may contain a multiplier, as in the example below:
Radius .word
.endstruct
-The size of the Circle struct is 6 (three words).
+
+In this example the size of the Circle struct is 6 (three words).
The storage allocator keywords
@@ -4919,7 +4920,7 @@ The size of the Circle struct is 6 (three words).
@@ -4972,13 +4982,54 @@ name=".TAG"> directive.
C: .tag Circle
-Currently, members are just offsets from the start of the struct or union. To
+Members are just offsets from the start of the struct or union. To
access a field of a struct, the member offset must be added to the address of
the struct variable itself:
- lda C+Circle::Radius ; Load circle radius into A
+ lda C + Circle::Radius ; Load circle radius
+ lda C + Circle::Origin + Point::ycoord ; Load circle origin.ycoord
-That may change in a future version of the assembler.
+
+Nested structures or unions are treated differently depending on whether they
+are anonymous. If named, a new structure definition is created within the
+enclosing scope, with its offsets beginning at 0. If anonymous, the members of
+the new structure are added to the enclosing scope instead, with offsets
+continuing through that scope. Example:
+
+
+ .struct Object
+ id .byte ; Object::id = 0
+ target .struct Point ; Object::target = 1
+ xcoord .word ; Object::Point::xcoord = 0
+ ycoord .word ; Object::Point::ycoord = 2
+ .endstruct
+ cost .struct ; Object::cost = 5
+ price .word ; Object::price = 5
+ tax .word ; Object::tax = 7
+ .endstruct
+ .struct
+ radius .word ; Object::radius = 9
+ .endstruct
+ .endstruct
+
+O: .tag Object
+ lda O + Object::target + Object::Point::ycoord ; Named struct
+ lda O + Object::tax ; Anonymous
+ lda O + Object::radius ; Anonymous
+
+ ; Be careful not to use a named nested structure without also adding the
+ ; offset to the nested structure itself.
+ lda O + Object::Point::ycoord ; Incorrect!
+ lda O + Object::target + Object::Point::ycoord ; Correct
+
+
+In this example, the first nested structure is named "Point", and its member
+offsets begin at 0. On the other hand, the two anonymous structures simply
+continue to add members to the enclosing "Object" structure.
+
+Note that an anonymous structure does not need a member name, since all of its
+members become part of the enclosing structure. The "cost" member in the
+example is redundantly the same offset as its first member "price".
Limitations
diff --git a/doc/cc65.sgml b/doc/cc65.sgml
index 37e3e493d..55b84ed5c 100644
--- a/doc/cc65.sgml
+++ b/doc/cc65.sgml
@@ -61,7 +61,7 @@ Short options:
-Os Inline some standard functions
-T Include source as comment
-V Print the compiler version number
- -W warning[,...] Suppress warnings
+ -W [-+]warning[,...] Control warnings ('-' disables, '+' enables)
-d Debug mode
-g Add debug info to object file
-h Help (this text)
@@ -84,8 +84,9 @@ Long options:
--create-full-dep name Create a full make dependency file
--data-name seg Set the name of the DATA segment
--debug Debug mode
+ --debug-tables name Write symbol table debug info to a file
--debug-info Add debug info to object file
- --debug-opt name Configure optimizations with a file
+ --debug-opt name Debug optimization steps
--debug-opt-output Debug output of each optimization step
--dep-target target Use this dependency target
--disable-opt name Disable an optimization step
@@ -823,6 +824,11 @@ and the one defined by the ISO standard:
as it sounds, since the 6502 has so few registers that it isn't
possible to keep values in registers anyway.
+- In
+ command line option.
+
There may be some more minor differences I'm currently not aware of. The
@@ -1047,6 +1053,16 @@ This cc65 version has some extensions to the ISO C standard.
unsigned char foo = 0b101; // sets it to 5
+- The character escape '\e', a GCC C extension, is accepted.
+ In ASCII this is the escape character 0x1B, which may be
+ remapped in other character sets via a #pragma charmap.
+ It can be disabled with the
option.
+
+
+ unsigned char foo = '\e'; // sets it to 0x1B or equivalent
+
+
@@ -1138,6 +1154,96 @@ The compiler defines several macros at startup:
+
+ __CPU__
+
+ This macro contains a bitset that allows to check if a specific instruction
+ set is supported. For example, the 65C02 CPU supports all instructions of the
+ 65SC02. So testing for the instruction set of the 65SC02 using the following
+ check will succeed for both CPUs (and also for the 65816 and HUC6280).
+
+
+ #if (__CPU__ & __CPU_ISET_65SC02__)
+
+
+ This is much simpler and more future proof than checking for specific CPUs.
+
+ The compiler defines a set of constants named /
+ command line option.
+
+ __CPU_6502__
+
+ This macro is defined if the code is compiled for a 6502 CPU.
+
+ __CPU_6502X__
+
+ This macro is defined if the code is compiled for a 6502 CPU with invalid
+ opcodes.
+
+ __CPU_6502DTV__
+
+ This macro is defined if the code is compiled for a DTV CPU.
+
+ __CPU_65SC02__
+
+ This macro is defined if the code is compiled for a 65SC02 CPU.
+
+ __CPU_65C02__
+
+ This macro is defined if the code is compiled for a 65C02 CPU.
+
+ __CPU_65816__
+
+ This macro is defined if the code is compiled for a 65816 CPU.
+
+ __CPU_HUC6280__
+
+ This macro is defined if the code is compiled for a HUC6280 CPU.
+
+ __CPU_ISET_6502__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 6502 CPU.
+
+ __CPU_ISET_6502X__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 6502X CPU.
+
+ __CPU_ISET_6502DTV__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 6502DTV CPU.
+
+ __CPU_ISET_65SC02__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 65SC02 CPU.
+
+ __CPU_ISET_65C02__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 65C02 CPU.
+
+ __CPU_ISET_65816__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the 65816 CPU.
+
+ __CPU_ISET_HUC6280__
+
+ This macro expands to a numeric constant that can be used to check the
+ / macro for the instruction set
+ of the HUC6280 CPU.
+
__CX16__
This macro is defined if the target is the Commander X16 (-t cx16).
@@ -1273,6 +1379,12 @@ If the first parameter is #pragma allow-eager-inline ([push,] on|off)
@@ -1358,7 +1470,7 @@ parameter with the
/* Use a space wherever an 'a' occurs in ISO-8859-1 source */
- #pragma charmap (0x61, 0x20);
+ #pragma charmap (0x61, 0x20)
@@ -1613,13 +1725,13 @@ parameter with the function will be used
to determine the number from the bank attribute defined in the linker config,
see . Note that
@@ -1629,6 +1741,11 @@ parameter with the for usage.
+url="tgi.html#tgi_load_vectorfont" name="tgi_load_vectorfont"> for usage.
diff --git a/doc/cl65.sgml b/doc/cl65.sgml
index 24d2f5927..f48e1353c 100644
--- a/doc/cl65.sgml
+++ b/doc/cl65.sgml
@@ -261,6 +261,9 @@ different options for different files on the command line. As an example.
translates main.c with full optimization and module.c with less optimization
and debug info enabled.
+Note that the target system (-t , --target) must be specified before any file
+unless using the default target of c64
+
The type of an input file is derived from its extension:
diff --git a/doc/customizing.sgml b/doc/customizing.sgml
index e18bcf86c..58631eb3c 100644
--- a/doc/customizing.sgml
+++ b/doc/customizing.sgml
@@ -140,7 +140,7 @@ FEATURES {
SYMBOLS {
# Define the stack size for the application
- __STACKSIZE__: value = $0200, weak = yes;
+ __STACKSIZE__: value = $0200, type = weak;
}
diff --git a/doc/cx16.sgml b/doc/cx16.sgml
index 78a51206b..a718e52fa 100644
--- a/doc/cx16.sgml
+++ b/doc/cx16.sgml
@@ -243,6 +243,12 @@ point to
+
+
+ This driver features a resolution of 640 across and 480 down with 2 colors,
+ black and white.
+
+
Extended memory drivers
diff --git a/doc/da65.sgml b/doc/da65.sgml
index 113eb6f97..94fbfbd29 100644
--- a/doc/da65.sgml
+++ b/doc/da65.sgml
@@ -255,7 +255,7 @@ disassembler may be told to recognize either the 65SC02 or 65C02 CPUs. The
latter understands the same opcodes as the former, plus 16 additional bit
manipulation and bit test-and-branch commands. Using 6502x as CPU the illegal
opcodes of 6502 CPU are detected and displayed. 6502dtv setting recognizes the
-emulated CPU instructons of the C64DTV device.
+emulated CPU instructions of the C64DTV device.
When disassembling 4510 code, due to handling of 16-bit wide branches, da65
diff --git a/doc/funcref.sgml b/doc/funcref.sgml
index 524818b19..5eab5adcd 100644
--- a/doc/funcref.sgml
+++ b/doc/funcref.sgml
@@ -71,18 +71,21 @@ function.
[
][
][
+][
][
][
][
][
][
][
+][
][
][
][
][
][
][
+][
][
][
]
@@ -92,8 +95,16 @@ function.
- _dos_type
+
- allow_lowercase
+
[
+][
+][
][
+][
+][
]- rebootafterexit
+
[
+][
]
@@ -101,9 +112,15 @@ function.
- _dos_type
+
[
+][
+][
][
+][
+][
]- rebootafterexit
[
+][
]
@@ -128,7 +145,7 @@ function.
[
][
-
+][
(incomplete)
@@ -145,6 +162,7 @@ function.
][
][
][
+][
@@ -214,7 +232,7 @@ function.
-
+][
][
][
@@ -335,7 +353,7 @@ function.
]
[
-
+][
@@ -430,7 +448,7 @@ see also ]testcode/lib/em-test.c and samples/multidemo.c .
-
+[
][
]
@@ -506,6 +524,14 @@ see also testcode/lib/em-test.c and samples/multidemo.c .
+
+
+
+[
+][
+]
+
+
@@ -537,7 +563,7 @@ see also testcode/lib/em-test.c and samples/multidemo.c .
-
+[
][
]
@@ -555,7 +581,7 @@ It does not declare any functions.
-
+[
][
]
@@ -725,7 +751,7 @@ communication, see also testcode/lib/ser-test.c .
[
][
][
-
+][
][
][
@@ -771,6 +797,7 @@ communication, see also ]testcode/lib/ser-test.c .
[
][
][
+][
][
][
][
@@ -846,6 +873,20 @@ communication, see also ]testcode/lib/ser-test.c .
(incomplete)
+
+
+
+[
+]
+
+
+
+
+
+[
+]
+
+
(incomplete)
@@ -863,6 +904,13 @@ communication, see also testcode/lib/ser-test.c .
(incomplete)
+
+
+
+[
+]
+
+
Alphabetical function reference
_DE_ISDIR
@@ -1750,10 +1798,11 @@ used in presence of a prototype.
/
+/
-- The function is specific to the Sym-1.
+
- The function is specific to the Sym-1 and Apple2 platforms.
-
@@ -2851,6 +2900,79 @@ setting the time may not work. See also the platform-specific information.
+gmtime_dt
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
- This function is only available on Apple II.
+
- On Apple II, you can't stat() an opened file. stat() before opening.
+
+
+
+#include
+#include
+#include
+int main(void)
+{
+ struct stat st;
+ struct tm* tm;
+ if (stat ("/disk/file", &st) == 0) {
+ tm = gmtime_dt (&st.st_ctime);
+ if (tm)
+ printf ("File created on %s\n", asctime(tm));
+ }
+}
+
+
+
+
+
+mktime_dt
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
- This function is only available on Apple II.
+
+
+
+#include
+#include
+#include
+int main(void)
+{
+ struct stat st;
+ if (stat ("/disk/file", &st) == 0) {
+ printf ("File created on %s\n",
+ localtime (mktime_dt (&st.st_ctime)));
+ }
+}
+
+
+
+
+
clrscr
@@ -3277,6 +3399,70 @@ used in presence of a prototype.
- Use LZ4_compress_HC with compression level 16 for best compression.
+
- Your program will need to know the uncompressed size of the buffer as
+there is no end-of-stream marker.
+
- LZ4 is the biggest and second-slowest decompressor shipped in cc65 runtime.
+It is also the least efficient compression algorithm.
+
+
+
+
+
+decompress_lzsa1
+
+
+
+/
+
+- Use
The project and compressor can be found at
+- LZSA1 is the fastest decompressor shipped in cc65 runtime, but data is less
+compressed than with LZSA2 and ZX02.
+
+
+
+
+
+decompress_lzsa2
+
+
+
+/
+
+- Use
The project and compressor can be found at
+- LZSA2 is the second fastest decompressor shipped in cc65 runtime, but data is less
+compressed than with ZX02.
+
+
+
+
+
+decompress_zx02
+
+
+
+/
+
+- Use
The project and compressor can be found at
+- ZX02 is the slowest decompressor shipped with cc65 runtime, but is also the
+smallest and has the best compression ratio.
+detect_iigs
+
+
+
+/
+
+- The function is specific to the Apple2 and Apple2enh platforms.
+
+
,
+[,
+]
+
+
+
detect_scpu
@@ -3404,6 +3610,25 @@ used in presence of a prototype.
+dir_entry_count
+
+
+
+/
+
+- The function does not exist on all platforms.
+
+
+
+
+
div
@@ -3967,6 +4192,31 @@ be used in presence of a prototype.
+get_tv
+
+
+
+,
+[, ][,
+][, ][,
+][, ][/
+]
+- The function does not exist on all platforms.
+
- Return TV_NTSC for 60Hz systems, TV_PAL for 50Hz systems, or
+TV_OTHER if the scan frequency can not be determined.
+
+
+
+
+
get_ostype
@@ -4078,6 +4328,27 @@ header files define constants that can be used to check the return code.
+get_iigs_speed
+
+
+
+/
+
+- The function is specific to the Apple2 and Apple2enh platforms.
+
- See the accelerator.h header for the speed definitions.
+
+
,
+[,
+]
+
+
+
get_scpu_speed
@@ -6061,6 +6332,32 @@ be used in presence of a prototype.
+posix_memalign
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
+
+
+
+
+
psg_delay
@@ -6229,6 +6526,9 @@ be used in presence of a prototype.
- The returned pointer may point to a statically allocated instance of
On the Apple II platform, the d_ctime and d_mtime returned are in the
+ProDOS format. You can convert them to more portable time representations using
+the ProDOS datetime conversion functions.
- On several platforms, namely the CBMs and the Atari, the disk drives get
confused when opening/closing files between directory reads. So for example a
program that reads the list of files on a disk, and after each call to
@@ -6893,6 +7193,30 @@ clean-up when exiting the program.
+set_iigs_speed
+
+
+
+/
+
+- The function is specific to the Apple2 and Apple2enh platforms.
+
- See the accelerator.h header for the speed definitions.
+
- Accepted parameters are SPEED_SLOW and SPEED_FAST (all other values are
+considered SPEED_FAST).
+
+
,
+[,
+]
+
+
+
set_scpu_speed
@@ -7075,6 +7399,85 @@ be used in presence of a prototype.
+stat
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
- On the Apple II platform, the st_ctim, st_mtim and st_atim members are left
+to zero, for size and performance reasons. The ProDOS creation and modification dates
+are returned in the ProDOS format in st_ctime and st_mtime. The access date does
+not exist. You can convert them to POSIX-style time representations using
+the
.
+
+
+
+#include <sys/stat.h>
+
+#define FILENAME "helloworld"
+struct stat stbuf;
+if (stat (FILENAME, &stbuf) == 0) {
+ printf ("%s size is %lu bytes (created on %s)\n", FILENAME, stbuf.st_size,
+#ifndef __APPLE2__
+ localtime (&stbuf.st_ctim.tv_sec)
+#else
+ localtime (mktime_dt (&stbuf.st_ctime))
+#endif
+ );
+} else {
+ printf ("There was a problem stat'ing %s: %d\n", FILENAME, errno);
+}
+
+
+
+
+
+statvfs
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
- The function requires an absolute pathname.
+
+
+
+#include <sys/statvfs.h>
+
+#define FILENAME "/disk/helloworld"
+struct statvfs stvbuf;
+if (statvfs (FILENAME, &stvbuf) == 0) {
+ printf ("%s filesystem has %u blocks of %u size, %u of them free.\n", FILENAME, stvbuf.f_blocks, stvbuf.f_bsize, stvbuf.f_bfree);
+} else {
+ printf ("There was a problem statvfs'ing %s: %d\n", FILENAME, errno);
+}
+
+
+
+
+
strcasecmp
@@ -7655,22 +8058,47 @@ be used in presence of a prototype.
-strstr
+strcasestr
-/
-
- The function is only available as fastcall function, so it may only
be used in presence of a prototype.
,
+[,
+][
+]
+
+
+
+strstr
+
+
+
+/
+
+- The function is only available as fastcall function, so it may only
+be used in presence of a prototype.
+
+
,
[,
][
],
[,
][,
][/
]unsigned __fastcall__ videomode (unsigned Mode); /* for apple2enh and c128 */
-signed char __fastcall__ videomode (signed char Mode); /* for cx16 */
+unsigned __fastcall__ videomode (unsigned Mode); /* for c128 */
+signed char __fastcall__ videomode (signed char Mode); /* for apple2 and cx16 */
-- The function is specific to the Commodore 128, the enhanced Apple //e,
+
- The function is specific to the Commodore 128, the Apple II,
and the Commander X16.
- This function replaces
[.
]- The function is available as only a fastcall function, so it may be used
only in the presence of a prototype.
+
- On Apple II, this functions returns the previously active video mode, or -1
+if the mode is not supported due to lack of hardware.
-
,
[,
@@ -8087,13 +8518,21 @@ only in the presence of a prototype.
]
,
+[,
][,
][,
][,
][/
-]void waitvsync (void);
+- The function will silently fail when the feature is not
+supported, like on the Apple ][+.
+
diff --git a/doc/ld65.sgml b/doc/ld65.sgml
index 1ad04b395..b889645f5 100644
--- a/doc/ld65.sgml
+++ b/doc/ld65.sgml
@@ -1180,6 +1180,202 @@ The ZPSAVE segment contains the original values of the zeropage locations used
by the ZEROPAGE segment. It is placed in its own segment because it must not be
initialized.
+Debug Info
+
+The debug info and the API mirrors closely the items available in the sources
+used to build an executable. To use the API efficiently, it is necessary to
+understand from which blocks the information is built.
+
+
+- Libraries
+
- Lines
+
- Modules
+
- Scopes
+
- Segments
+
- Source files
+
- Spans
+
- Symbols
+
- Types
+
+
+Each item of each type has something like a primary index called an 'id'.
+The ids can be thought of as array indices, so looking up something by its
+id is fast. Invalid ids are marked with the special value CC65_INV_ID.
+Data passed back for an item may contain ids of other objects. A scope for
+example contains the id of the parent scope (or CC65_INV_ID if there is no
+parent scope). Most API functions use ids to lookup related objects.
+
+
+Libraries
+
+This information comes from the linker and is currently used in only one
+place:To mark the origin of a module. The information available for a library
+is its name including the path.
+
+
+- Library id
+
- Name and path of library
+
+
+
+Lines
+
+A line is a location in a source file. It is module dependent, which means
+that if two modules use the same source file, each one has its own line
+information for this file. While the assembler has also column information,
+it is dropped early because it would generate much more data. A line may have
+one or more spans attached if code or data is generated.
+
+
+- Line id
+
- Id of the source file, the line is from
+
- The line number in the file (starting with 1)
+
- The type of the line: Assembler/C source or macro
+
- A count for recursive macros if the line comes from a macro
+
+
+
+Modules
+
+A module is actually an object file. It is generated from one or more source
+files and may come from a library. The assembler generates a main scope for
+symbols declared outside user generated scopes. The main scope has an empty name.
+
+
+- Module id
+
- The name of the module including the path
+
- The id of the main source file (the one specified on the command line)
+
- The id of the library the module comes from, or CC65_INV_ID
+
- The id of the main scope for this module
+
+
+
+Scopes
+
+Each module has a main scope where all symbols live, that are specified outside
+other scopes. Additional nested scopes may be specified in the sources. So scopes
+have a one to many relation: Each scope (with the exception of the main scope) has
+exactly one parent and may have several child scopes. Scopes may not cross modules.
+
+
+- Scope id
+
- The name of the scope (may be empty)
+
- The type of the scope: Module, .SCOPE or .PROC, .STRUCT and .ENUM
+
- The size of the scope (the size of the span for the active segment)
+
- The id of the parent scope (CC65_INV_ID in case of the main scope)
+
- The id of the attached symbol for .PROC scopes
+
- The id of the module where the scope comes from
+
+
+
+Segment Info
+
+
+- Segment id
+
- The name of the segment
+
- The start address of the segment
+
- The size of the segment
+
- The name of the output file, this segment was written to (may be empty)
+
- The offset of the segment in the output file (only if name not empty)
+
- The bank number of the segment's memory area
+
+
+It is also possible to retrieve the spans for sections (a section is the part of a
+segment that comes from one module). Since the main scope covers a whole module, and
+the main scope has spans assigned (if not empty), the spans for the main scope of a
+module are also the spans for the sections in the segments.
+
+
+Source files
+
+Modules are generated from source files. Since some source files are used several times
+when generating a list of modules (header files for example), the linker will merge
+duplicates to reduce redundant information. Source files are considered identical if the
+full name including the path is identical, and the size and time of last modification
+matches. Please note that there may be still duplicates if files are accessed using
+different paths.
+
+
+- Source file id
+
- The name of the source file including the path
+
- The size of the file at the time when it was read
+
- The time of last modification at the time when the file was read
+
+
+
+Spans
+
+A span is a small part of a segment. It has a start address and a size. Spans are used
+to record sizes of other objects. Line infos and scopes may have spans attached, so it
+is possible to lookup which data was generated for these items.
+
+
+- Span id
+
- The start address of the span. This is an absolute address
+
- The end address of the span. This is inclusive which means if start==end then => size==1
+
- The id of the segment were the span is located
+
- The type of the data in the span (optional, maybe NULL)
+
- The number of line infos available for this span
+
- The number of scope infos available for this span
+
+
+The last two fields will save a call to cc65_line_byspan or cc65_scope_byspan by providing
+information about the number of items that can be retrieved by these calls.
+
+
+Symbols
+
+
+- Symbol id
+
- The name of the symbol
+
- The type of the symbol, which may be label, equate or import
+
- The size of the symbol (size of attached code or data). Only for labels. Zero if unknown
+
- The value of the symbol. For an import, this is taken from the corresponding export
+
- The id of the corresponding export. Only valid for imports, CC65_INV_ID for other symbols
+
- The segment id if the symbol is segment based. For an import, taken from the export
+
- The id of the scope this symbols was defined in
+
- The id of the parent symbol. This is only set for cheap locals and CC65_INV_ID otherwise
+
+
+Beware: Even for an import, the id of the corresponding export may be CC65_INV_ID.
+This happens if the module with the export has no debug information. So make sure
+that your application can handle it.
+
+
+Types
+
+A type is somewhat special. You cannot retrieve data about it in a similar way as with the other
+items. Instead you have to call a special routine that parses the type data and returns it
+in a set of data structures that can be processed by a C or C++ program.
+
+The type information is language independent and doesn't encode things like 'const' or
+'volatile'. Instead it defines a set of simple data types and a few ways to aggregate
+them (arrays, structs and unions).
+
+Type information is currently generated by the assembler for storage allocating commands
+like .BYTE or .WORD. For example, the assembler code
+
+
+foo: .byte $01, $02, $03
+
+
+will assign the symbol foo a size of 3, but will also generate a span with a size of 3
+bytes and a type ARRAY[3] OF BYTE.
+Evaluating the type of a span allows a debugger to display the data in the same way as it
+was defined in the assembler source.
+
+
+
+
+.ADDR| ARRAY OF LITTLE ENDIAN POINTER WITH SIZE 2 TO VOID@
+.BYTE| ARRAY OF UNSIGNED WITH SIZE 1@
+.DBYT| ARRAY OF BIG ENDIAN UNSIGNED WITH SIZE 2@
+.DWORD| ARRAY OF LITTLE ENDIAN UNSIGNED WITH SIZE 4@
+.FARADDR| ARRAY OF LITTLE ENDIAN POINTER WITH SIZE 3 TO VOID@
+.WORD| ARRAY OF LITTLE ENDIAN UNSIGNED WITH SIZE 2
+
+
+
Copyright
diff --git a/doc/sim65.sgml b/doc/sim65.sgml
index c2740bbad..9c3764e1d 100644
--- a/doc/sim65.sgml
+++ b/doc/sim65.sgml
@@ -40,6 +40,8 @@ The simulator is called as follows:
Long options:
--help Help (this text)
--cycles Print amount of executed CPU cycles
+ --cpu Override CPU type (6502, 65C02, 6502X)
+ --trace Enable CPU trace
--verbose Increase verbosity
--version Print the simulator version number
@@ -70,6 +72,17 @@ Here is a description of all the command line options:
count.
+ --cpu <type>
+
+ Specify the CPU type to use while executing the program. This CPU type
+ is normally determined from the program file header, but it can be useful
+ to override it.
+
+ --trace
+
+ Print a single line of information for each instruction or interrupt that
+ is executed by the CPU to stdout.
+
-v, --verbose
Increase the simulator verbosity.
@@ -115,37 +128,78 @@ PVExit ($01)
Creating a Test in C
-For a C test compiled and linked with ).
+a set of built-in paravirtualization functions (see [).
+Example:
+
+]
+#include
+int main()
+{
+ printf("Hello!\n");
+ return 5;
+}
+
+// Build and run:
+// cl65 -t sim6502 -o example.prg example.c
+// sim65 example.prg
+
+// Build and run, separate steps:
+// cc65 -t sim6502 -o example.s example.c
+// ca65 -t sim6502 -o example.o example.s
+// ld65 -t sim6502 -o example.prg example.o sim6502.lib
+// sim65 example.prg
+
Creating a Test in Assembly
-Assembly tests may similarly be assembled and linked with
-
- Return from
-The binary file has a 12 byte header:
+Example:
+
+
+.export _main
+_main:
+ lda #5
+ rts
+
+; Build and run:
+; cl65 -t sim6502 -o example.prg example.s
+; sim65 example.prg
+
+; Build and run, separate steps:
+; ca65 -t sim6502 -o example.o example.s
+; ld65 -t sim6502 -o example.prg example.o sim6502.lib
+; sim65 example.prg
+
+
+Internally, the binary program file has a 12 byte header provided by the library:
@@ -182,8 +236,204 @@ These use cc65 calling conventions, and are intended for use with the sim65 targ
The
+Counter peripheral
+
+The sim65 simulator supports a memory-mapped counter peripheral that manages
+a number of 64-bit counters that are continuously updated as the simulator is
+running. For each counter, it also provides a 64 bit "latching" register.
+
+
The functionality of the counter peripheral is accessible through 3 registers:
+
+
+PERIPHERALS_COUNTER_LATCH ($FFC0, write-only)
+PERIPHERALS_COUNTER_SELECT ($FFC1, read/write)
+PERIPHERALS_COUNTER_VALUE ($FFC2..$FFC9, read-only)
+
+
+
These three registers are used as follows.
+
+
When a program explicitly requests a "counter latch" operation by writing any value
+to the PERIPHERALS_COUNTER_LATCH address ($FFC0), all live registers are simultaneously
+copied to the latch registers. They will keep their newly latched values until another latch
+operation is requested.
+
+
The PERIPHERALS_COUNTER_SELECT address ($FFC1) register holds an 8-bit value that
+specifies which 64-bit latch register is currently readable through the PERIPHERALS_COUNTER_VALUE
+address range. Six values are currently defined:
+
+
+- $00: latched clock cycle counter selected.
+
- $01: latched CPU instruction counter selected.
+
- $02: latched IRQ interrupt counter selected.
+
- $03: latched NMI interrupt counter selected.
+
- $80: latched wallclock time (nanoseconds) selected.
+
- $81: latched wallclock time (split: seconds, nanoseconds) selected.
+
+
+
Values $00 to $03 provide access to the latched (frozen) value of their respective live
+counters at the time of the last write to PERIPHERALS_COUNTER_LATCH .
+
+
When PERIPHERALS_COUNTER_SELECT equals $80, the PERIPHERALS_COUNTER_VALUE
+will be a 64-bit value corresponding to the number of nanoseconds elapsed since the Unix epoch
+(Midnight, Jan 1st, 1970 UTC), at the time of the last latch operation.
+
+
When PERIPHERALS_COUNTER_SELECT equals $81, the high 32 bits of PERIPHERALS_COUNTER_VALUE
+will be a 32-bit value corresponding to the number of seconds elapsed since the Unix epoch (Midnight, Jan 1st,
+1970 UTC), at the time of the last latch operation. The low 32 bits of
+PERIPHERALS_COUNTER_VALUE will hold the nanoseconds since the start of that second.
+
+
The two different wallclock-time latch registers will always refer to precisely the same time instant.
+For some applications, the single 64-bit value measured in nanoseconds will be more convenient, while
+for other applications, the split 32/32 bits representation with separate second and nanosecond
+values will be more convenient.
+
+
Note that the time elapsed since the Unix epoch is an approximation, as the implementation depends on the
+way POSIX defines time-since-the-epoch. Unfortunately, POSIX incorrectly assumes that all days are precisely
+86400 seconds long, which is not true in case of leap seconds. The way this inconsistency is resolved is
+system dependent.
+
+
On reset, PERIPHERALS_COUNTER_SELECT is initialized to zero. If the PERIPHERALS_COUNTER_SELECT
+register holds a value other than one of the six values described above, all PERIPHERALS_COUNTER_VALUE
+bytes will read as zero.
+
+
The PERIPHERALS_COUNTER_VALUE addresses ($FFC2..$FFC9) are used to read to currently
+selected 64-bit latch register value. Address $FFC2 holds the least significant byte (LSB),
+while address $FFC9 holds the most significant byte (MSB).
+
+
On reset, all latch registers are reset to zero. Reading any of the PERIPHERALS_COUNTER_VALUE
+bytes before the first write to PERIPHERALS_COUNTER_LATCH will yield zero.
+
+Example:
+
+
+/* This example uses the peripheral support in sim65.h */
+
+#include
+#include
+
+static void print_current_counters(void)
+{
+ peripherals.counter.latch = 0; /* latch values */
+
+ peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER;
+ printf("clock cycles ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
+ peripherals.counter.select = COUNTER_SELECT_INSTRUCTION_COUNTER;
+ printf("instructions ............... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
+ peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME;
+ printf("wallclock time ............. : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
+ peripherals.counter.select = COUNTER_SELECT_WALLCLOCK_TIME_SPLIT;
+ printf("wallclock time, split ...... : %08lx %08lx\n", peripherals.counter.value32[1], peripherals.counter.value32[0]);
+ printf("\n");
+}
+
+int main(void)
+{
+ print_current_counters();
+ print_current_counters();
+ return 0;
+}
+
+
+SIM65 control peripheral
+
+The sim65 simulator supports a memory-mapped peripheral that allows control
+of the simulator behavior itself.
+
+
The sim65 control peripheral interface consists of 2 registers:
+
+
+PERIPHERALS_SIMCONTROL_CPUMODE ($FFCA, read/write)
+PERIPHERALS_SIMCONTROL_TRACEMODE ($FFCB, read/write)
+
+
+
Address PERIPHERALS_SIMCONTROL_CPUMODE allows access to the currently active CPU mode.
+
+
Possible values are CPU_6502 (0), CPU_65C02 (1), and CPU_6502X (2). For specialized applications,
+it may be useful to switch CPU models at runtime; this is supported by writing 0, 1, or 2 to this address.
+Writing any other value will be ignored.
+
+
Address PERIPHERALS_SIMCONTROL_TRACEMODE allows inspection and control of the currently active
+CPU tracing mode.
+
+
A value of 0 means tracing is disabled; a value of $7F fully enables tracing. The 7
+lower bits of the value actually provide control over which fields are printed; see below
+for an explanation of the seven fields.
+
+
Having the ability to enable/disable tracing on the fly can be a useful debugging aid. For example,
+it can be used to enable tracing for short fragments of code. Consider the following example:
+
+
+/* This example uses the TRACE_ON and TRACE_OFF macros defined in sim65.h */
+
+#include
+#include
+
+unsigned x;
+
+int main(void)
+{
+ TRACE_ON();
+
+ x = 0x1234; /* We want to see what happens here. */
+
+ TRACE_OFF();
+
+ return 0;
+}
+
+
+
This small test program, when compiled with optimizations enabled (-O), produces the output trace below:
+
+
+70 232 022E A2 12 ldx #$12 A=7F X=00 Y=04 S=FD Flags=nvdizC SP=FFBC
+71 234 0230 A9 34 lda #$34 A=7F X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
+72 236 0232 8D C8 02 sta $02C8 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
+73 240 0235 8E C9 02 stx $02C9 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
+74 244 0238 A9 00 lda #$00 A=34 X=12 Y=04 S=FD Flags=nvdizC SP=FFBC
+75 246 023A 8D CB FF sta $FFCB A=00 X=12 Y=04 S=FD Flags=nvdiZC SP=FFBC
+
+
+
The example output shows the full trace format, consisting of the following seven fields:
+
+
+- The first field is an instruction counter. We see here that the assignment '
x = 0x1234; '
+starts at the 70th CPU instruction since the start of the simulator, and takes four 6502 instructions.
+The two instructions that follow correspond to the execution of the TRACE_OFF ' macro
+that disables tracing.
+- The second field shows the clock cycles since the start of the program. Here we see that the
+first four instructions take 12 clock cycles in total (262 - 250 = 12).
+
- The third field shows the program counter as a four-digit, i.e., the PC register. Its 16-bit
+ value is displayed as a 4-digit hecadecimal number.
+
- The fourth field shows one to three hexadecimal byte values that make up the instruction.
+
- The fifth field shows the instruction in human-readable assembly language.
+
- The sixth field shows the CPU registers before execution of the instruction. The A, X, Y, and
+ S registers are each shown as a single byte value. The six status bits of the CPU are shown in
+ the order NVDIZC (Negative, Overflow, Decimal, Interrupt, Zero, Carry). They are displayed as
+ a capital letter if the flag is set, or a small letter if the flag is unset.
+
- The seventh and last field shows the software stack pointer SP as used by CC65 programs that
+ conform to the CC65 conventions.
+
+
+
Writing a specific value to PERIPHERALS_SIMCONTROL_TRACEMODE will control which of these
+seven fields are displayed. The following values are defined to denote the seven fields:
+
+
+- TRACE_FIELD_INSTR_COUNTER = 0x40
+
- TRACE_FIELD_CLOCK_COUNTER = 0x20
+
- TRACE_FIELD_PC = 0x10
+
- TRACE_FIELD_INSTR_BYTES = 0x08
+
- TRACE_FIELD_INSTR_ASSEMBLY = 0x04
+
- TRACE_FIELD_CPU_REGISTERS = 0x02
+
- TRACE_FIELD_CC65_SP = 0x01
+
+
+
For example, writing the value $16 to PERIPHERALS_SIMCONTROL_TRACEMODE will only display
+the program counter, instruction assembly, and CPU registers fields.
Copyright
diff --git a/doc/tgi.sgml b/doc/tgi.sgml
index 29acd8ce6..3b013664f 100644
--- a/doc/tgi.sgml
+++ b/doc/tgi.sgml
@@ -477,10 +477,10 @@ be used in presence of a prototype.
-/
#include
@@ -82,7 +83,77 @@
#define CH_CURS_LEFT 0x08
#define CH_CURS_RIGHT 0x15
-#if !defined(__APPLE2ENH__)
+/* These characters are not available on the ][+, but
+ * are on the //e. */
+#if defined(__APPLE2ENH__) || defined(APPLE2_INCLUDE_IIE_CHARS)
+#define CH_DEL 0x7F
+#define CH_CURS_UP 0x0B
+#define CH_CURS_DOWN 0x0A
+
+/* These are defined to be OpenApple + NumberKey */
+#define CH_F1 0xB1
+#define CH_F2 0xB2
+#define CH_F3 0xB3
+#define CH_F4 0xB4
+#define CH_F5 0xB5
+#define CH_F6 0xB6
+#define CH_F7 0xB7
+#define CH_F8 0xB8
+#define CH_F9 0xB9
+#define CH_F10 0xB0
+#endif
+
+#if defined(__APPLE2ENH__)
+
+/* MouseText-based functions for boxes and lines drawing */
+void mt_chline (unsigned char length);
+void mt_cvline (unsigned char length);
+void mt_chlinexy (unsigned char x, unsigned char y, unsigned char length);
+void mt_cvlinexy (unsigned char x, unsigned char y, unsigned char length);
+
+#define CH_HLINE 0x5F
+#define CH_VLINE 0xDF
+#define CH_ULCORNER 0x5F
+#define CH_URCORNER 0x20
+#define CH_LLCORNER 0xD4
+#define CH_LRCORNER 0xDF
+#define CH_TTEE 0x5F
+#define CH_BTEE 0xD4
+#define CH_LTEE 0xD4
+#define CH_RTEE 0xDF
+#define CH_CROSS 0xD4
+
+#define _chline(length) mt_chline(length)
+#define _chlinexy(x,y,length) mt_chlinexy(x,y,length)
+#define _cvline(length) mt_cvline(length)
+#define _cvlinexy(x,y,length) mt_cvlinexy(x,y,length)
+
+#else
+
+/* Functions that don't depend on MouseText to draw boxes and lines */
+void dyn_chline (unsigned char h, unsigned char length);
+void dyn_cvline (unsigned char v, unsigned char length);
+void dyn_chlinexy (unsigned char h, unsigned char x, unsigned char y, unsigned char length);
+void dyn_cvlinexy (unsigned char v, unsigned char x, unsigned char y, unsigned char length);
+
+#if defined(DYN_BOX_DRAW)
+/* When the user defines DYN_BOX_DRAW, we'll adapt to the machine
+** we run on.
+ */
+extern char CH_HLINE;
+extern char CH_VLINE;
+extern char CH_ULCORNER;
+extern char CH_URCORNER;
+extern char CH_LLCORNER;
+extern char CH_LRCORNER;
+extern char CH_TTEE;
+extern char CH_BTEE;
+extern char CH_LTEE;
+extern char CH_RTEE;
+extern char CH_CROSS;
+
+#else
+/* Otherwise, fallback to safety and don't use MouseText at all. */
#define CH_HLINE '-'
#define CH_VLINE '!'
#define CH_ULCORNER '+'
@@ -94,7 +165,14 @@
#define CH_LTEE '+'
#define CH_RTEE '+'
#define CH_CROSS '+'
-#endif
+#endif /* DYN_BOX_DRAW */
+
+#define _chline(length) dyn_chline(CH_HLINE, length)
+#define _chlinexy(x, y, length) dyn_chlinexy(CH_HLINE, x ,y, length)
+#define _cvline(length) dyn_cvline(CH_VLINE, length)
+#define _cvlinexy(x, y, length) dyn_cvlinexy(CH_VLINE, x, y, length)
+
+#endif /* __APPLE2ENH__ */
/* Masks for joy_read */
#define JOY_UP_MASK 0x10
@@ -121,6 +199,17 @@
#define APPLE_IIGS1 0x81 /* Apple IIgs (ROM 1) */
#define APPLE_IIGS3 0x83 /* Apple IIgs (ROM 3) */
+/* Return codes for get_tv() */
+#define TV_NTSC 0
+#define TV_PAL 1
+#define TV_OTHER 2
+
+/* Video modes */
+#define VIDEOMODE_40x24 0x15
+#define VIDEOMODE_80x24 0x00
+#define VIDEOMODE_40COL VIDEOMODE_40x24
+#define VIDEOMODE_80COL VIDEOMODE_80x24
+
extern unsigned char _dos_type;
/* Valid _dos_type values:
**
@@ -142,6 +231,27 @@ extern unsigned char _dos_type;
** ProDOS 8 2.4.x - 0x24
*/
+/* struct stat.st_mode values */
+#define S_IFDIR 0x01
+#define S_IFREG 0x02
+#define S_IFBLK 0xFF
+#define S_IFCHR 0xFF
+#define S_IFIFO 0xFF
+#define S_IFLNK 0xFF
+#define S_IFSOCK 0xFF
+
+struct datetime {
+ struct {
+ unsigned day :5;
+ unsigned mon :4;
+ unsigned year :7;
+ } date;
+ struct {
+ unsigned char min;
+ unsigned char hour;
+ } time;
+};
+
/*****************************************************************************/
@@ -151,20 +261,10 @@ extern unsigned char _dos_type;
/* The file stream implementation and the POSIX I/O functions will use the
-** following struct to set the date and time stamp on files. This specificially
+** following struct to set the date and time stamp on files. This specifically
** applies to the open and fopen functions.
*/
-extern struct {
- struct {
- unsigned day :5;
- unsigned mon :4;
- unsigned year :7;
- } createdate; /* Current date: 0 */
- struct {
- unsigned char min;
- unsigned char hour;
- } createtime; /* Current time: 0 */
-} _datetime;
+extern struct datetime _datetime;
/* The addresses of the static drivers */
#if !defined(__APPLE2ENH__)
@@ -185,6 +285,12 @@ extern void a2_lo_tgi[];
+void beep (void);
+/* Beep beep. */
+
+unsigned char get_tv (void);
+/* Get the machine vblank frequency. Returns one of the TV_xxx codes. */
+
unsigned char get_ostype (void);
/* Get the machine type. Returns one of the APPLE_xxx codes. */
@@ -211,6 +317,35 @@ void rebootafterexit (void);
#define _cpeekcolor() COLOR_WHITE
#define _cpeekrevers() 0
+struct tm* __fastcall__ gmtime_dt (const struct datetime* dt);
+/* Converts a ProDOS date/time structure to a struct tm */
+
+time_t __fastcall__ mktime_dt (const struct datetime* dt);
+/* Converts a ProDOS date/time structure to a time_t UNIX timestamp */
+
+typedef struct DIR DIR;
+
+unsigned int __fastcall__ dir_entry_count(DIR *dir);
+/* Returns the number of active files in a ProDOS directory */
+
+#if !defined(__APPLE2ENH__)
+unsigned char __fastcall__ allow_lowercase (unsigned char onoff);
+/* If onoff is 0, lowercase characters printed to the screen via STDIO and
+** CONIO are forced to uppercase. If onoff is 1, lowercase characters are
+** printed to the screen untouched. By default lowercase characters are
+** forced to uppercase because a stock Apple ][+ doesn't support lowercase
+** display. The function returns the old lowercase setting.
+*/
+#endif
+
+signed char __fastcall__ videomode (unsigned mode);
+/* Set the video mode, return the old mode, or -1 if 80-column hardware is not
+** installed. Call with one of the VIDEOMODE_xx constants.
+*/
+
+void waitvsync (void);
+/* Wait for start of next frame */
+
/* End of apple2.h */
diff --git a/include/apple2enh.h b/include/apple2enh.h
index 3989d0b8d..84e6f4ab3 100644
--- a/include/apple2enh.h
+++ b/include/apple2enh.h
@@ -46,49 +46,6 @@
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-/* Characters codes */
-#define CH_DEL 0x7F
-#define CH_CURS_UP 0x0B
-#define CH_CURS_DOWN 0x0A
-
-#define CH_HLINE 0x5F
-#define CH_VLINE 0xDF
-#define CH_ULCORNER 0x5F
-#define CH_URCORNER 0x20
-#define CH_LLCORNER 0xD4
-#define CH_LRCORNER 0xDF
-#define CH_TTEE 0x5F
-#define CH_BTEE 0xD4
-#define CH_LTEE 0xD4
-#define CH_RTEE 0xDF
-#define CH_CROSS 0xD4
-
-/* These are defined to be OpenApple + NumberKey */
-#define CH_F1 0xB1
-#define CH_F2 0xB2
-#define CH_F3 0xB3
-#define CH_F4 0xB4
-#define CH_F5 0xB5
-#define CH_F6 0xB6
-#define CH_F7 0xB7
-#define CH_F8 0xB8
-#define CH_F9 0xB9
-#define CH_F10 0xB0
-
-/* Video modes */
-#define VIDEOMODE_40x24 0x0011
-#define VIDEOMODE_80x24 0x0012
-#define VIDEOMODE_40COL VIDEOMODE_40x24
-#define VIDEOMODE_80COL VIDEOMODE_80x24
-
-
-
/*****************************************************************************/
/* Variables */
/*****************************************************************************/
@@ -106,21 +63,5 @@ extern void a2e_lo_tgi[];
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-unsigned __fastcall__ videomode (unsigned mode);
-/* Set the video mode, return the old mode. Call with one of the VIDEOMODE_xx
-** constants.
-*/
-
-void waitvsync (void);
-/* Wait for start of next frame */
-
-
-
/* End of apple2enh.h */
#endif
diff --git a/include/arpa/inet.h b/include/arpa/inet.h
index cd353a2bb..3d715766f 100644
--- a/include/arpa/inet.h
+++ b/include/arpa/inet.h
@@ -40,6 +40,20 @@
/*****************************************************************************/
+#if (__CPU__ & __CPU_ISET_65SC02__)
+/* Always inline, three bytes is not more than a jsr */
+
+#define ntohs(x) \
+ ( \
+ __AX__=(x), \
+ asm("phx"), \
+ asm("tax"), \
+ asm("pla"), \
+ __AX__ \
+ )
+#define htons(x) ntohs(x)
+
+#else
#if (__OPT_i__ < 200)
int __fastcall__ ntohs (int val);
@@ -56,12 +70,12 @@ int __fastcall__ htons (int val);
)
#define htons(x) ntohs(x)
-#endif
+#endif /* __OPT_i__ < 200 */
+
+#endif /* __CPU__ & __CPU_ISET_65SC02__ */
long __fastcall__ ntohl (long val);
long __fastcall__ htonl (long val);
-
-
/* End of arpa/inet.h */
#endif
diff --git a/include/atari.h b/include/atari.h
index 04cacab33..0af109264 100644
--- a/include/atari.h
+++ b/include/atari.h
@@ -220,17 +220,17 @@
/* Color register functions */
/*****************************************************************************/
-extern void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminance);
-extern void __fastcall__ _setcolor_low (unsigned char color_reg, unsigned char color_value);
-extern unsigned char __fastcall__ _getcolor (unsigned char color_reg);
+void __fastcall__ _setcolor (unsigned char color_reg, unsigned char hue, unsigned char luminance);
+void __fastcall__ _setcolor_low (unsigned char color_reg, unsigned char color_value);
+unsigned char __fastcall__ _getcolor (unsigned char color_reg);
/*****************************************************************************/
/* Other screen functions */
/*****************************************************************************/
-extern void waitvsync (void); /* wait for start of next frame */
-extern int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */
-extern void __fastcall__ _scroll (signed char numlines);
+void waitvsync (void); /* wait for start of next frame */
+int __fastcall__ _graphics (unsigned char mode); /* mode value same as in BASIC */
+void __fastcall__ _scroll (signed char numlines);
/* numlines > 0 scrolls up */
/* numlines < 0 scrolls down */
@@ -239,18 +239,18 @@ extern void __fastcall__ _scroll (signed char numlines);
/* Sound function */
/*****************************************************************************/
-extern void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume);
+void __fastcall__ _sound (unsigned char voice, unsigned char frequency, unsigned char distortion, unsigned char volume);
/*****************************************************************************/
/* Misc. functions */
/*****************************************************************************/
-extern unsigned char get_ostype(void); /* get ROM version */
-extern unsigned char get_tv(void); /* get TV system */
-extern void _save_vecs(void); /* save system vectors */
-extern void _rest_vecs(void); /* restore system vectors */
-extern char *_getdefdev(void); /* get default floppy device */
-extern unsigned char _is_cmdline_dos(void); /* does DOS support command lines */
+unsigned char get_ostype(void); /* get ROM version */
+unsigned char get_tv(void); /* get TV system */
+void _save_vecs(void); /* save system vectors */
+void _rest_vecs(void); /* restore system vectors */
+char *_getdefdev(void); /* get default floppy device */
+unsigned char _is_cmdline_dos(void); /* does DOS support command lines */
/*****************************************************************************/
diff --git a/include/atari5200.h b/include/atari5200.h
index ff176c15b..9a0399d0e 100644
--- a/include/atari5200.h
+++ b/include/atari5200.h
@@ -94,7 +94,7 @@ extern void atr5200std_joy[]; /* referred to by joy_static_stddrv[] */
#define _bordercolor(color) 0
/* wait for start of next frame */
-extern void waitvsync (void);
+void waitvsync (void);
/* end of atari5200.h */
#endif
diff --git a/include/atari7800.h b/include/atari7800.h
index 3cbeedb8b..b289bb41e 100644
--- a/include/atari7800.h
+++ b/include/atari7800.h
@@ -52,7 +52,7 @@
/* No support for dynamically loadable drivers */
#define DYN_DRV 0
-extern unsigned char get_tv(void); /* get TV system */
+unsigned char get_tv(void); /* get TV system */
#include <_tia.h>
#define TIA (*(struct __tia*)0x0000)
diff --git a/include/atmos.h b/include/atmos.h
index 38d423c46..9b1b021c1 100644
--- a/include/atmos.h
+++ b/include/atmos.h
@@ -170,6 +170,20 @@ void atmos_zap (void);
/* Raygun sound effect */
+/* The following #defines will cause the matching function prototypes
+** in conio.h to be overlaid by macroes with the same names,
+** thereby saving the function call overhead.
+*/
+#define _textcolor(color) COLOR_WHITE
+#define _bgcolor(color) COLOR_BLACK
+#define _bordercolor(color) COLOR_BLACK
+#define _cpeekcolor(color) COLOR_WHITE
+
+
+void waitvsync (void);
+/* Wait for start of next frame */
+
+
/* End of atmos.h */
#endif
diff --git a/include/cbm264.h b/include/cbm264.h
index 4951df518..ab634b721 100644
--- a/include/cbm264.h
+++ b/include/cbm264.h
@@ -97,7 +97,7 @@
#define COLOR_WHITE (BCOLOR_WHITE | CATTR_LUMA7)
#define COLOR_RED (BCOLOR_RED | CATTR_LUMA4)
#define COLOR_CYAN (BCOLOR_CYAN | CATTR_LUMA7)
-#define COLOR_PURPLE (BCOLOR_VIOLET | CATTR_LUMA7)
+#define COLOR_PURPLE (BCOLOR_LIGHTVIOLET | CATTR_LUMA7)
#define COLOR_GREEN (BCOLOR_GREEN | CATTR_LUMA7)
#define COLOR_BLUE (BCOLOR_BLUE | CATTR_LUMA7)
#define COLOR_YELLOW (BCOLOR_YELLOW | CATTR_LUMA7)
diff --git a/include/conio.h b/include/conio.h
index bac20e3c5..cf84d1742 100644
--- a/include/conio.h
+++ b/include/conio.h
@@ -216,7 +216,18 @@ void __fastcall__ cputhex16 (unsigned val);
# define cpeekrevers() _cpeekrevers()
#endif
-
+#ifdef _chline
+# define chline(len) _chline(len)
+#endif
+#ifdef _cvline
+# define cvline(len) _cvline(len)
+#endif
+#ifdef _chlinexy
+# define chlinexy(x, y, len) _chlinexy(x, y, len)
+#endif
+#ifdef _cvlinexy
+# define cvlinexy(x, y, len) _cvlinexy(x, y, len)
+#endif
/* End of conio.h */
#endif
diff --git a/include/cx16.h b/include/cx16.h
index 5bbd21247..6a3705631 100644
--- a/include/cx16.h
+++ b/include/cx16.h
@@ -3,7 +3,7 @@
/* cx16.h */
/* */
/* CX16 system-specific definitions */
-/* For prerelease 39 */
+/* For prerelease 43 */
/* */
/* */
/* This software is provided "as-is", without any expressed or implied */
@@ -176,6 +176,11 @@ enum {
#define VIDEOMODE_40x15 0x04
#define VIDEOMODE_20x30 0x05
#define VIDEOMODE_20x15 0x06
+#define VIDEOMODE_22x23 0x07
+#define VIDEOMODE_64x50 0x08
+#define VIDEOMODE_64x25 0x09
+#define VIDEOMODE_32x50 0x0A
+#define VIDEOMODE_32x25 0x0B
#define VIDEOMODE_80COL VIDEOMODE_80x60
#define VIDEOMODE_40COL VIDEOMODE_40x30
#define VIDEOMODE_320x240 0x80
diff --git a/include/dirent.h b/include/dirent.h
index 124c7f224..60982ba05 100644
--- a/include/dirent.h
+++ b/include/dirent.h
@@ -33,6 +33,8 @@
#ifndef _DIRENT_H
#define _DIRENT_H
+#include
+
/*****************************************************************************/
@@ -46,31 +48,15 @@ typedef struct DIR DIR;
#if defined(__APPLE2__)
struct dirent {
- char d_name[16];
- unsigned d_ino;
- unsigned d_blocks;
- unsigned long d_size;
- unsigned char d_type;
- struct {
- unsigned day :5;
- unsigned mon :4;
- unsigned year :7;
- } d_cdate;
- struct {
- unsigned char min;
- unsigned char hour;
- } d_ctime;
- unsigned char d_access;
- unsigned d_auxtype;
- struct {
- unsigned day :5;
- unsigned mon :4;
- unsigned year :7;
- } d_mdate;
- struct {
- unsigned char min;
- unsigned char hour;
- } d_mtime;
+ char d_name[16];
+ unsigned d_ino;
+ unsigned d_blocks;
+ unsigned long d_size;
+ unsigned char d_type;
+ struct datetime d_ctime;
+ unsigned char d_access;
+ unsigned d_auxtype;
+ struct datetime d_mtime;
};
#define _DE_ISREG(t) ((t) != 0x0F)
@@ -161,7 +147,5 @@ void __fastcall__ seekdir (DIR* dir, long offs);
void __fastcall__ rewinddir (DIR* dir);
-
-
/* End of dirent.h */
#endif
diff --git a/include/lynx.h b/include/lynx.h
index 41dc5acb3..259b3da71 100644
--- a/include/lynx.h
+++ b/include/lynx.h
@@ -31,27 +31,15 @@
/* */
/*****************************************************************************/
-
-
#ifndef _LYNX_H
#define _LYNX_H
-
-
/* Check for errors */
#if !defined(__LYNX__)
# error This module may only be used when compiling for the Lynx game console!
#endif
-
-
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-/* Color defines */
+/* Color definitions */
#define COLOR_TRANSPARENT 0x00
#define COLOR_BLACK 0x01
#define COLOR_RED 0x02
@@ -88,6 +76,56 @@
#define TGI_COLOR_LIGHTBLUE COLOR_LIGHTBLUE
#define TGI_COLOR_WHITE COLOR_WHITE
+/* No support for dynamically loadable drivers */
+#define DYN_DRV 0
+
+/* Addresses of static drivers */
+extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */
+extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */
+extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */
+
+/* Sound support */
+void lynx_snd_init (void); /* Initialize the sound driver */
+void lynx_snd_pause (void); /* Pause sound */
+void lynx_snd_continue (void); /* Continue sound after pause */
+void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music); /* Play tune on channel */
+void lynx_snd_stop (void); /* Stop sound on all channels */
+void __fastcall__ lynx_snd_stop_channel (unsigned char channel); /* Stop sound on all channels */
+unsigned char lynx_snd_active(void); /* Show which channels are active */
+
+/* Cartridge access */
+void __fastcall__ lynx_load (int file_number); /* Load a file into RAM using a zero-based index */
+void __fastcall__ lynx_exec (int file_number); /* Load a file into ram and execute it */
+
+/* EEPROM access */
+unsigned __fastcall__ lynx_eeprom_read (unsigned char cell); /* Read a 16 bit word from the given address */
+unsigned __fastcall__ lynx_eeprom_write (unsigned char cell, unsigned val); /* Write the word at the given address */
+void __fastcall__ lynx_eeprom_erase (unsigned char cell); /* Clear the word at the given address */
+unsigned __fastcall__ lynx_eeread (unsigned cell); /* Read a 16 bit word from the given address 93C46, 93C66 or 93C86 */
+unsigned __fastcall__ lynx_eewrite (unsigned cell, unsigned val); /* Write the word at the given address 93C46, 93C66 or 93C86 */
+
+/* TGI extras */
+#define tgi_sprite(spr) tgi_ioctl(0, spr)
+#define tgi_flip() tgi_ioctl(1, (void*)0)
+#define tgi_setbgcolor(bgcol) tgi_ioctl(2, (void*)(bgcol))
+#define tgi_setframerate(rate) tgi_ioctl(3, (void*)(rate))
+#define tgi_busy() tgi_ioctl(4, (void*)0)
+#define tgi_updatedisplay() tgi_ioctl(4, (void*)1)
+#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active))
+
+/* Hardware definitions */
+#include <_mikey.h>
+#define MIKEY (*(struct __mikey *)0xFD00)
+
+#define _MIKEY_TIMERS (*(struct _mikey_all_timers *) 0xFD00) /* mikey_timers[8] */
+#define _HBL_TIMER (*(struct _mikey_timer *) 0xFD00) /* timer0 (HBL) */
+#define _VBL_TIMER (*(struct _mikey_timer *) 0xFD08) /* timer2 (VBL) */
+#define _UART_TIMER (*(struct _mikey_timer *) 0xFD14) /* timer4 (UART) */
+#define _VIDDMA (*(unsigned int *) 0xFD92) /* DISPCTL/VIDDMA */
+
+#include <_suzy.h>
+#define SUZY (*(volatile struct __suzy*)0xFC00)
+
/* Masks for joy_read */
#define JOY_UP_MASK 0x80
#define JOY_DOWN_MASK 0x40
@@ -102,118 +140,5 @@
#define JOY_BTN_A(v) ((v) & JOY_BTN_A_MASK)
#define JOY_BTN_B(v) ((v) & JOY_BTN_B_MASK)
-/* No support for dynamically loadable drivers */
-#define DYN_DRV 0
-
-
-
-/*****************************************************************************/
-/* Variables */
-/*****************************************************************************/
-
-
-
-/* The addresses of the static drivers */
-extern void lynx_stdjoy_joy[]; /* Referred to by joy_static_stddrv[] */
-extern void lynx_comlynx_ser[]; /* Referred to by ser_static_stddrv[] */
-extern void lynx_160_102_16_tgi[]; /* Referred to by tgi_static_stddrv[] */
-
-
-
-/*****************************************************************************/
-/* Sound support */
-/*****************************************************************************/
-
-
-
-void lynx_snd_init (void);
-/* Initialize the sound driver */
-
-void lynx_snd_pause (void);
-/* Pause sound */
-
-void lynx_snd_continue (void);
-/* Continue sound after pause */
-
-void __fastcall__ lynx_snd_play (unsigned char channel, unsigned char *music);
-/* Play tune on channel */
-
-void lynx_snd_stop (void);
-/* Stop sound on all channels */
-
-void __fastcall__ lynx_snd_stop_channel (unsigned char channel);
-/* Stop sound on all channels */
-
-unsigned char lynx_snd_active(void);
-/* Show which channels are active */
-
-
-
-/*****************************************************************************/
-/* Accessing the cart */
-/*****************************************************************************/
-
-
-
-void __fastcall__ lynx_load (int fileno);
-/* Load a file into ram. The first entry is fileno=0. */
-
-void __fastcall__ lynx_exec (int fileno);
-/* Load a file into ram and execute it. */
-
-
-
-/*****************************************************************************/
-/* Accessing the EEPROM */
-/*****************************************************************************/
-
-
-
-unsigned __fastcall__ lynx_eeprom_read (unsigned char cell);
-/* Read a 16 bit word from the given address */
-
-unsigned __fastcall__ lynx_eeprom_write (unsigned char cell, unsigned val);
-/* Write the word at the given address */
-
-void __fastcall__ lynx_eeprom_erase (unsigned char cell);
-/* Clear the word at the given address */
-
-unsigned __fastcall__ lynx_eeread (unsigned cell);
-/* Read a 16 bit word from the given address 93C46 93C66 or 93C86*/
-
-unsigned __fastcall__ lynx_eewrite (unsigned cell, unsigned val);
-/* Write the word at the given address 93C46 93C66 or 93C86*/
-
-
-
-/*****************************************************************************/
-/* TGI extras */
-/*****************************************************************************/
-
-
-
-#define tgi_sprite(spr) tgi_ioctl(0, spr)
-#define tgi_flip() tgi_ioctl(1, (void*)0)
-#define tgi_setbgcolor(bgcol) tgi_ioctl(2, (void*)(bgcol))
-#define tgi_setframerate(rate) tgi_ioctl(3, (void*)(rate))
-#define tgi_busy() tgi_ioctl(4, (void*)0)
-#define tgi_updatedisplay() tgi_ioctl(4, (void*)1)
-#define tgi_setcollisiondetection(active) tgi_ioctl(5, (void*)(active))
-
-/* Define Hardware */
-#include <_mikey.h>
-#define MIKEY (*(struct __mikey *)0xFD00)
-
-#define _MIKEY_TIMERS (*(struct _mikey_all_timers *) 0xFD00) // mikey_timers[8]
-#define _HBL_TIMER (*(struct _mikey_timer *) 0xFD00) // timer0 (HBL)
-#define _VBL_TIMER (*(struct _mikey_timer *) 0xFD08) // timer2 (VBL)
-#define _UART_TIMER (*(struct _mikey_timer *) 0xFD14) // timer4 (UART)
-#define _VIDDMA (*(unsigned int *) 0xFD92) // dispctl/viddma
-
-#include <_suzy.h>
-#define SUZY (*(struct __suzy*)0xFC00)
-
-
-
/* End of lynx.h */
#endif
diff --git a/include/lzsa.h b/include/lzsa.h
new file mode 100644
index 000000000..9b0eb82e5
--- /dev/null
+++ b/include/lzsa.h
@@ -0,0 +1,56 @@
+/*****************************************************************************/
+/* */
+/* lzsa.h */
+/* */
+/* Decompression routine for the 'lzsa' format */
+/* */
+/* */
+/* */
+/* (C) 2022 John Brandwood */
+/* */
+/* */
+/* Boost license: */
+/* Distributed under the Boost Software License, Version 1.0. */
+/* Boost Software License - Version 1.0 - August 17th, 2003 */
+/* */
+/* Permission is hereby granted, free of charge, to any person or */
+/* organization */
+/* obtaining a copy of the software and accompanying documentation covered by*/
+/* this license (the "Software") to use, reproduce, display, distribute, */
+/* execute, and transmit the Software, and to prepare derivative works of the*/
+/* Software, and to permit third-parties to whom the Software is furnished to*/
+/* do so, all subject to the following: */
+/* */
+/* The copyright notices in the Software and this entire statement, including*/
+/* the above license grant, this restriction and the following disclaimer, */
+/* must be included in all copies of the Software, in whole or in part, and */
+/* all derivative works of the Software, unless such copies or derivative */
+/* works are solely in the form of machine-executable object code generated */
+/* by a source language processor. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*/
+/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */
+/* FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT */
+/* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE */
+/* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR */
+/* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE */
+/* USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*****************************************************************************/
+
+
+
+#ifndef _LZSA_H
+#define _LZSA_H
+
+void __fastcall__ decompress_lzsa1 (const unsigned char* src, unsigned char* const dst);
+/* Decompresses the source buffer into the destination buffer.
+** compress with lzsa -r -f 1 input.bin output.lzsa1
+ */
+
+void __fastcall__ decompress_lzsa2 (const unsigned char* src, unsigned char* const dst);
+/* Decompresses the source buffer into the destination buffer.
+** compress with lzsa -r -f 2 input.bin output.lzsa2
+ */
+
+/* end of lzsa.h */
+#endif
diff --git a/include/rp6502.h b/include/rp6502.h
index 033684b72..7deeebc4c 100644
--- a/include/rp6502.h
+++ b/include/rp6502.h
@@ -47,8 +47,7 @@ struct __RP6502
unsigned char step1;
unsigned int addr1;
unsigned char xstack;
- unsigned char errno_lo;
- unsigned char errno_hi;
+ unsigned int errno;
unsigned char op;
unsigned char irq;
const unsigned char spin;
@@ -72,8 +71,8 @@ void __fastcall__ ria_push_long (unsigned long val);
void __fastcall__ ria_push_int (unsigned int val);
#define ria_push_char(v) RIA.xstack = v
-long __fastcall__ ria_pop_long (void);
-int __fastcall__ ria_pop_int (void);
+long ria_pop_long (void);
+int ria_pop_int (void);
#define ria_pop_char() RIA.xstack
/* Set the RIA fastcall register */
@@ -101,6 +100,7 @@ long __fastcall__ ria_call_long_errno (unsigned char op);
#define RIA_OP_CODEPAGE 0x03
#define RIA_OP_LRAND 0x04
#define RIA_OP_STDIN_OPT 0x05
+#define RIA_OP_CLOCK 0x0F
#define RIA_OP_CLOCK_GETRES 0x10
#define RIA_OP_CLOCK_GETTIME 0x11
#define RIA_OP_CLOCK_SETTIME 0x12
@@ -117,10 +117,12 @@ long __fastcall__ ria_call_long_errno (unsigned char op);
/* C API for the operating system. */
+int __cdecl__ xregn (char device, char channel, unsigned char address, unsigned count,
+ ...);
int __cdecl__ xreg (char device, char channel, unsigned char address, ...);
-int __fastcall__ phi2 (void);
-int __fastcall__ codepage (void);
-long __fastcall__ lrand (void);
+int phi2 (void);
+int codepage (void);
+long lrand (void);
int __fastcall__ stdin_opt (unsigned long ctrl_bits, unsigned char str_length);
int __fastcall__ read_xstack (void* buf, unsigned count, int fildes);
int __fastcall__ read_xram (unsigned buf, unsigned count, int fildes);
diff --git a/include/sim65.h b/include/sim65.h
new file mode 100644
index 000000000..ef59955ff
--- /dev/null
+++ b/include/sim65.h
@@ -0,0 +1,136 @@
+/*****************************************************************************/
+/* */
+/* sim65.h */
+/* */
+/* Definitions for the sim6502 and sim65c02 targets */
+/* */
+/* */
+/* */
+/* (C) 2025 Sidney Cadot */
+/* */
+/* */
+/* 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. */
+/* */
+/*****************************************************************************/
+
+#ifndef _SIM65_H
+#define _SIM65_H
+
+/* Check that we include this file while compiling to a compatible target. */
+#if !defined(__SIM6502__) && !defined(__SIM65C02__)
+# error This module may only be used when compiling for the sim6502 or sim65c02 targets!
+#endif
+
+#include
+
+/* The sim65 targets (sim6502, sim65c02) have a peripheral memory aperture located at
+ * address range 0xFFC0 .. 0xFFDF. Currently, the following peripherals are located
+ * inside that memory apeture:
+ *
+ * $FFC0 .. $FFC9 "counter" peripheral
+ * $FFCA .. $FFCB "sim65 control" peripheral
+ * $FFCC .. $FFDF (currently unused)
+ *
+ * The "peripherals" structure below corresponds to the register layout of the currently
+ * defined peripherals in this memory range. Combined with the fact that the sim6502 and
+ * sim65c02 linker configuration files define the "peripherals" symbol to be fixed at
+ * address $FFC0, this provides easy-to-use and efficient access to the peripheral registers.
+ *
+ * After including "sim65.h", it is possible for a C program to do things like:
+ *
+ * {
+ * peripherals.counter.latch = 0;
+ * peripherals.sim65.cpu_mode = SIM65_CPU_MODE_6502X;
+ * peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL;
+ * }
+ *
+ * Note that "peripherals" variable is declared volatile. This instructs a C compiler to
+ * forego optimizations on memory accesses to the variable. However, CC65 currently ignores
+ * the volatile attribute. Fortunately, it is not smart with respect to optimizing
+ * memory accesses, so accessing the "peripherals" fields works fine in practice.
+ */
+
+extern volatile struct {
+ struct {
+ uint8_t latch;
+ uint8_t select;
+ union {
+ uint8_t value [8]; /* Access value as eight separate bytes. */
+ uint16_t value16 [4]; /* Access value as four 16-bit words. */
+ uint32_t value32 [2]; /* Access value as two 32-bit long words. */
+ };
+ } counter;
+ struct {
+ uint8_t cpu_mode;
+ uint8_t trace_mode;
+ } sim65;
+} peripherals;
+
+/* Values for the peripherals.counter.select field. */
+#define COUNTER_SELECT_CLOCKCYCLE_COUNTER 0x00
+#define COUNTER_SELECT_INSTRUCTION_COUNTER 0x01
+#define COUNTER_SELECT_IRQ_COUNTER 0x02
+#define COUNTER_SELECT_NMI_COUNTER 0x03
+#define COUNTER_SELECT_WALLCLOCK_TIME 0x80
+#define COUNTER_SELECT_WALLCLOCK_TIME_SPLIT 0x81
+
+/* Values for the peripherals.sim65.cpu_mode field. */
+#define SIM65_CPU_MODE_6502 0x00
+#define SIM65_CPU_MODE_65C02 0x01
+#define SIM65_CPU_MODE_6502X 0x02
+
+/* Bitfield values for the peripherals.sim65.trace_mode field. */
+#define SIM65_TRACE_MODE_FIELD_INSTR_COUNTER 0x40
+#define SIM65_TRACE_MODE_FIELD_CLOCK_COUNTER 0x20
+#define SIM65_TRACE_MODE_FIELD_PC 0x10
+#define SIM65_TRACE_MODE_FIELD_INSTR_BYTES 0x08
+#define SIM65_TRACE_MODE_FIELD_INSTR_ASSEMBLY 0x04
+#define SIM65_TRACE_MODE_FIELD_CPU_REGISTERS 0x02
+#define SIM65_TRACE_MODE_FIELD_CC65_SP 0x01
+
+/* Values for the peripherals.sim65.trace_mode field that fully disable / enable tracing. */
+#define SIM65_TRACE_MODE_DISABLE 0x00
+#define SIM65_TRACE_MODE_ENABLE_FULL 0x7F
+
+/* Convenience macros to enable / disable tracing at runtime. */
+#define TRACE_ON() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_ENABLE_FULL; while(0)
+#define TRACE_OFF() do peripherals.sim65.trace_mode = SIM65_TRACE_MODE_DISABLE; while(0)
+
+/* Convenience macro to query the CPU mode at runtime. */
+#define GET_CPU_MODE() peripherals.sim65.cpu_mode
+
+/* Convenience macro to set the CPU mode at runtime.
+ *
+ * Use SIM65_CPU_MODE_6502, SIM65_CPU_MODE_65C02, or SIM65_CPU_MODE_6502 as argument.
+ *
+ * Important Note:
+ *
+ * When running in a program compiled for the "sim6502" target, it is safe to switch to
+ * 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and
+ * those work the same in 65C02 and 6502X mode.
+ *
+ * However, when running in a program compiled for the "sim65c02" target, it is NOT safe
+ * to switch to 6502 or 6502X mode, since many routines in the runtime library use
+ * 65C02-specific opcodes, and these will not work as expected when the CPU is switched
+ * to 6502 or 6502X mode. When such an instruction is encountered, the program will
+ * exhibit undefined behavior.
+ */
+#define SET_CPU_MODE(mode) do peripherals.sim65.cpu_mode = mode; while(0)
+
+/* End of sim65.h */
+#endif
diff --git a/include/stdint.h b/include/stdint.h
index 5d6f04769..6d51565e0 100644
--- a/include/stdint.h
+++ b/include/stdint.h
@@ -52,15 +52,15 @@ typedef unsigned char uint8_t;
typedef unsigned uint16_t;
typedef unsigned long uint32_t;
-#define INT8_MIN ((int8_t) 0x80)
-#define INT8_MAX ((int8_t) 0x7F)
-#define INT16_MIN ((int16_t) 0x8000)
-#define INT16_MAX ((int16_t) 0x7FFF)
-#define INT32_MIN ((int32_t) 0x80000000)
-#define INT32_MAX ((int32_t) 0x7FFFFFFF)
-#define UINT8_MAX ((uint8_t) 0xFF)
-#define UINT16_MAX ((uint16_t) 0xFFFF)
-#define UINT32_MAX ((uint32_t) 0xFFFFFFFF)
+#define INT8_MIN -128
+#define INT8_MAX 127
+#define INT16_MIN (-32767 - 1)
+#define INT16_MAX 32767
+#define INT32_MIN (-2147483647L - 1L)
+#define INT32_MAX 2147483647L
+#define UINT8_MAX 255
+#define UINT16_MAX 65535U
+#define UINT32_MAX 4294967295UL
/* Minimum-width integer types */
typedef signed char int_least8_t;
@@ -70,15 +70,15 @@ typedef unsigned char uint_least8_t;
typedef unsigned uint_least16_t;
typedef unsigned long uint_least32_t;
-#define INT_LEAST8_MIN ((int_least8_t) 0x80)
-#define INT_LEAST8_MAX ((int_least8_t) 0x7F)
-#define INT_LEAST16_MIN ((int_least16_t) 0x8000)
-#define INT_LEAST16_MAX ((int_least16_t) 0x7FFF)
-#define INT_LEAST32_MIN ((int_least32_t) 0x80000000)
-#define INT_LEAST32_MAX ((int_least32_t) 0x7FFFFFFF)
-#define UINT_LEAST8_MAX ((uint_least8_t) 0xFF)
-#define UINT_LEAST16_MAX ((uint_least16_t) 0xFFFF)
-#define UINT_LEAST32_MAX ((uint_least32_t) 0xFFFFFFFF)
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
/* Fastest minimum-width integer types */
typedef signed char int_fast8_t;
@@ -88,40 +88,40 @@ typedef unsigned char uint_fast8_t;
typedef unsigned uint_fast16_t;
typedef unsigned long uint_fast32_t;
-#define INT_FAST8_MIN ((int_fast8_t) 0x80)
-#define INT_FAST8_MAX ((int_fast8_t) 0x7F)
-#define INT_FAST16_MIN ((int_fast16_t) 0x8000)
-#define INT_FAST16_MAX ((int_fast16_t) 0x7FFF)
-#define INT_FAST32_MIN ((int_fast32_t) 0x80000000)
-#define INT_FAST32_MAX ((int_fast32_t) 0x7FFFFFFF)
-#define UINT_FAST8_MAX ((uint_fast8_t) 0xFF)
-#define UINT_FAST16_MAX ((uint_fast16_t) 0xFFFF)
-#define UINT_FAST32_MAX ((uint_fast32_t) 0xFFFFFFFF)
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
/* Integer types capable of holding object pointers */
typedef int intptr_t;
typedef unsigned uintptr_t;
-#define INTPTR_MIN ((intptr_t)0x8000)
-#define INTPTR_MAX ((intptr_t)0x7FFF)
-#define UINTPTR_MAX ((uintptr_t) 0xFFFF)
+#define INTPTR_MIN INT16_MIN
+#define INTPTR_MAX INT16_MAX
+#define UINTPTR_MAX UINT16_MAX
/* Greatest width integer types */
typedef long intmax_t;
typedef unsigned long uintmax_t;
-#define INTMAX_MIN ((intmax_t) 0x80000000)
-#define INTMAX_MAX ((intmax_t) 0x7FFFFFFF)
-#define UINTMAX_MAX ((uintmax_t) 0xFFFFFFFF)
+#define INTMAX_MIN INT32_MIN
+#define INTMAX_MAX INT32_MAX
+#define UINTMAX_MAX UINT32_MAX
/* Limits of other integer types */
-#define PTRDIFF_MIN ((int) 0x8000)
-#define PTRDIFF_MAX ((int) 0x7FFF)
+#define PTRDIFF_MIN INT16_MIN
+#define PTRDIFF_MAX INT16_MAX
-#define SIG_ATOMIC_MIN ((unsigned char) 0x00)
-#define SIG_ATOMIC_MAX ((unsigned char) 0xFF)
+#define SIG_ATOMIC_MIN 0
+#define SIG_ATOMIC_MAX UINT8_MAX
-#define SIZE_MAX 0xFFFF
+#define SIZE_MAX UINT16_MAX
/* Macros for minimum width integer constants */
#define INT8_C(c) c
diff --git a/include/stdio.h b/include/stdio.h
index 012b8e2ba..35ebd7784 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -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
diff --git a/include/string.h b/include/string.h
index abaf80e7d..3b7ece1d9 100644
--- a/include/string.h
+++ b/include/string.h
@@ -81,6 +81,7 @@ void __fastcall__ bzero (void* ptr, size_t n); /* BSD */
char* __fastcall__ strdup (const char* s); /* SYSV/BSD */
int __fastcall__ stricmp (const char* s1, const char* s2); /* DOS/Windows */
int __fastcall__ strcasecmp (const char* s1, const char* s2); /* Same for Unix */
+char* __fastcall__ strcasestr (const char* str, const char* substr);
int __fastcall__ strnicmp (const char* s1, const char* s2, size_t count); /* DOS/Windows */
int __fastcall__ strncasecmp (const char* s1, const char* s2, size_t count); /* Same for Unix */
size_t __fastcall__ strnlen (const char* s, size_t maxlen); /* POSIX.1-2008 */
@@ -89,6 +90,7 @@ char* __fastcall__ strlower (char* s);
char* __fastcall__ strupr (char* s);
char* __fastcall__ strupper (char* s);
char* __fastcall__ strqtok (char* s1, const char* s2);
+char* __fastcall__ stpcpy (char* dest, const char* src);
#endif
const char* __fastcall__ __stroserror (unsigned char errcode);
diff --git a/include/sys/stat.h b/include/sys/stat.h
index d8fc09c75..0e1589d52 100644
--- a/include/sys/stat.h
+++ b/include/sys/stat.h
@@ -2,7 +2,7 @@
/* */
/* stat.h */
/* */
-/* Constants for the mode argument of open and creat */
+/* stat(2) definition */
/* */
/* */
/* */
@@ -11,6 +11,9 @@
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
+/* (C) 2023 Colin Leroy-Mira */
+/* EMail: colin@colino.net */
+/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
@@ -36,6 +39,10 @@
#ifndef _STAT_H
#define _STAT_H
+#include
+#include
+#include
+
/*****************************************************************************/
@@ -47,6 +54,30 @@
#define S_IREAD 0x01
#define S_IWRITE 0x02
+#define S_IFMT 0x03
+
+struct stat {
+ dev_t st_dev;
+ ino_t st_ino;
+ mode_t st_mode;
+ nlink_t st_nlink;
+ uid_t st_uid;
+ gid_t st_gid;
+ off_t st_size;
+ struct timespec st_atim;
+ struct timespec st_ctim;
+ struct timespec st_mtim;
+ #ifdef __APPLE2__
+ unsigned char st_access;
+ unsigned char st_type;
+ unsigned int st_auxtype;
+ unsigned char st_storagetype;
+ unsigned int st_blocks;
+ struct datetime st_mtime;
+ struct datetime st_ctime;
+ #endif
+};
+
/*****************************************************************************/
@@ -55,5 +86,9 @@
+int __fastcall__ stat (const char* pathname, struct stat* statbuf);
+
+
+
/* End of stat.h */
#endif
diff --git a/libsrc/common/asctime.c b/include/sys/statvfs.h
similarity index 70%
rename from libsrc/common/asctime.c
rename to include/sys/statvfs.h
index b46f29128..d9edc2f23 100644
--- a/libsrc/common/asctime.c
+++ b/include/sys/statvfs.h
@@ -1,15 +1,13 @@
/*****************************************************************************/
/* */
-/* asctime.c */
+/* statvfs.h */
/* */
-/* Convert a broken down time into a string */
+/* statvfs(3) definition */
/* */
/* */
/* */
-/* (C) 2002 Ullrich von Bassewitz */
-/* Wacholderweg 14 */
-/* D-70597 Stuttgart */
-/* EMail: uz@musoftware.de */
+/* (C) 2023 Colin Leroy-Mira */
+/* EMail: colin@colino.net */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
@@ -33,8 +31,32 @@
-#include
-#include
+#ifndef _STATVFS_H
+#define _STATVFS_H
+
+#include
+
+
+
+/*****************************************************************************/
+/* Data */
+/*****************************************************************************/
+
+
+
+struct statvfs {
+ unsigned long f_bsize;
+ unsigned long f_frsize;
+ fsblkcnt_t f_blocks;
+ fsblkcnt_t f_bfree;
+ fsblkcnt_t f_bavail;
+ fsfilcnt_t f_files;
+ fsfilcnt_t f_ffree;
+ fsfilcnt_t f_favail;
+ unsigned long f_fsid;
+ unsigned long f_flag;
+ unsigned long f_namemax;
+};
@@ -42,18 +64,11 @@
/* 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];
+int __fastcall__ statvfs (const char* pathname, struct statvfs* buf);
- /* Format into given buffer and return the result */
- return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0;
-}
+
+
+/* End of statvfs.h */
+#endif
diff --git a/include/sys/types.h b/include/sys/types.h
index e75dd7d46..89b91c5b4 100644
--- a/include/sys/types.h
+++ b/include/sys/types.h
@@ -50,6 +50,46 @@
typedef long int off_t;
#endif
+#ifndef _HAVE_dev_t
+#define _HAVE_dev_t
+typedef unsigned long int dev_t;
+#endif
+
+#ifndef _HAVE_ino_t
+#define _HAVE_ino_t
+typedef unsigned long int ino_t;
+#endif
+
+#ifndef _HAVE_nlink_t
+#define _HAVE_nlink_t
+typedef unsigned long int nlink_t;
+#endif
+
+#ifndef _HAVE_uid_t
+#define _HAVE_uid_t
+typedef unsigned char uid_t;
+#endif
+
+#ifndef _HAVE_gid_t
+#define _HAVE_gid_t
+typedef unsigned char gid_t;
+#endif
+
+#ifndef _HAVE_mode_t
+#define _HAVE_mode_t
+typedef unsigned char mode_t;
+#endif
+
+#ifndef _HAVE_fsblkcnt_t
+#define _HAVE_fsblkcnt_t
+typedef unsigned long int fsblkcnt_t;
+#endif
+
+#ifndef _HAVE_fsfilcnt_t
+#define _HAVE_fsfilcnt_t
+typedef unsigned long int fsfilcnt_t;
+#endif
+
/*****************************************************************************/
@@ -60,6 +100,3 @@ typedef long int off_t;
/* End of types.h */
#endif
-
-
-
diff --git a/include/time.h b/include/time.h
index bfc2ac435..5eb6f144a 100644
--- a/include/time.h
+++ b/include/time.h
@@ -37,6 +37,11 @@
#define _TIME_H
+/* Forward declaration for target.h */
+typedef unsigned long time_t;
+typedef unsigned long clock_t;
+
+
/* NULL pointer */
#ifndef NULL
@@ -49,9 +54,6 @@
typedef unsigned size_t;
#endif
-typedef unsigned long time_t;
-typedef unsigned long clock_t;
-
/* Structure for broken down time */
struct tm {
int tm_sec;
@@ -84,6 +86,8 @@ struct tm {
# define CLOCKS_PER_SEC 135 /* FIXME */
#elif defined(__GEOS__)
# define CLOCKS_PER_SEC 1
+#elif defined (__RP6502__)
+# define CLOCKS_PER_SEC 100
#elif defined(__TELESTRAT__)
# define CLOCKS_PER_SEC 10
#elif defined(__ATARI__) || defined (__LYNX__)
diff --git a/include/zx02.h b/include/zx02.h
new file mode 100644
index 000000000..b3e711d56
--- /dev/null
+++ b/include/zx02.h
@@ -0,0 +1,44 @@
+/*****************************************************************************/
+/* */
+/* zx02.h */
+/* */
+/* Decompression routine for the 'zx02' format */
+/* */
+/* */
+/* */
+/* (C) 2022 DMSC */
+/* */
+/* */
+/* MIT license: */
+/* Permission is hereby granted, free of charge, to any person obtaining a */
+/* copy of this software and associated documentation files (the “Software”),*/
+/* to deal in the Software without restriction, including without limitation */
+/* the rights to use, copy, modify, merge, publish, distribute, sublicense, */
+/* and/or sell copies of the Software, and to permit persons to whom the */
+/* Software is furnished to do so, subject to the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be included */
+/* in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS */
+/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN */
+/* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, */
+/* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR */
+/* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE */
+/* USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*****************************************************************************/
+
+
+
+#ifndef _ZX02_H
+#define _ZX02_H
+
+void __fastcall__ decompress_zx02 (const unsigned char* src, unsigned char* const dst);
+/* Decompresses the source buffer into the destination buffer.
+** compress with zx02 input.bin output.zx02
+ */
+
+
+/* end of zx02.h */
+#endif
diff --git a/libsrc/NameClashes.md b/libsrc/NameClashes.md
new file mode 100644
index 000000000..ef2105602
--- /dev/null
+++ b/libsrc/NameClashes.md
@@ -0,0 +1,380 @@
+List of cc65 library name clashes
+=================================
+
+The following is a list of identifiers that might need
+to be fixed, sorted by directory and identifier:
+
+# common
+
+## \_\_argc
+
+* libsrc/runtime/callmain.s
+* libsrc/cbm610/mainargs.s
+* libsrc/cx16/mainargs.s
+* libsrc/plus4/mainargs.s
+* libsrc/lynx/mainargs.s
+* libsrc/c16/mainargs.s
+* libsrc/geos-common/system/mainargs.s
+* libsrc/sim6502/mainargs.s
+* libsrc/c128/mainargs.s
+* libsrc/vic20/mainargs.s
+* libsrc/nes/mainargs.s
+* libsrc/atari/getargs.s
+* libsrc/apple2/mainargs.s
+* libsrc/cbm510/mainargs.s
+* libsrc/telestrat/mainargs.s
+* libsrc/c64/mainargs.s
+* libsrc/pet/mainargs.s
+* libsrc/atmos/mainargs.s
+
+## \_\_argv
+
+* libsrc/runtime/callmain.s
+* libsrc/cbm610/mainargs.s
+* libsrc/cx16/mainargs.s
+* libsrc/plus4/mainargs.s
+* libsrc/lynx/mainargs.s
+* libsrc/c16/mainargs.s
+* libsrc/geos-common/system/mainargs.s
+* libsrc/sim6502/mainargs.s
+* libsrc/c128/mainargs.s
+* libsrc/vic20/mainargs.s
+* libsrc/nes/mainargs.s
+* libsrc/atari/getargs.s
+* libsrc/apple2/mainargs.s
+* libsrc/cbm510/mainargs.s
+* libsrc/telestrat/mainargs.s
+* libsrc/c64/mainargs.s
+* libsrc/pet/mainargs.s
+* libsrc/atmos/mainargs.s
+
+## \_\_cos
+
+* libsrc/common/sincos.s
+
+## \_\_ctypeidx
+
+* libsrc/common/ctype.s
+* libsrc/common/ctypemask.s
+* libsrc/geos-common/system/ctype.s
+* libsrc/atari/ctype.s
+* libsrc/cbm/ctype.s
+* libsrc/atmos/ctype.s
+* asminc/ctype\_common.inc
+
+## \_\_cwd
+
+* libsrc/common/getcwd.s
+* libsrc/common/_cwd.s
+* libsrc/atari/initcwd.s
+* libsrc/apple2/initcwd.s
+* libsrc/apple2/initcwd.s
+* libsrc/telestrat/initcwd.s
+* libsrc/cbm/initcwd.s
+
+## \_\_cwd\_buf\_size
+
+* libsrc/common/_cwd.s
+
+## \_\_envcount
+
+* libsrc/common/searchenv.s
+* libsrc/common/_environ.s
+* libsrc/common/putenv.s
+* libsrc/common/getenv.s
+
+## \_\_environ
+
+* libsrc/common/searchenv.s
+* libsrc/common/_environ.s
+* libsrc/common/putenv.s
+* libsrc/common/getenv.s
+
+## \_\_envsize
+
+* libsrc/common/_environ.s
+* libsrc/common/putenv.s
+
+## \_\_fdesc
+
+* libsrc/common/_fdesc.s
+* libsrc/common/fopen.s
+
+## \_\_filetab
+
+* libsrc/common/_fdesc.s
+* libsrc/common/_file.s
+* asminc/_file.inc
+
+## \_\_fopen
+
+* libsrc/common/fopen.s
+* libsrc/common/_fopen.s
+
+## \_\_printf
+
+* libsrc/common/vsnprintf.s
+* libsrc/common/_printf.s
+* libsrc/common/vfprintf.s
+* libsrc/conio/vcprintf.s
+* libsrc/pce/_printf.s
+
+## \_\_scanf
+
+* libsrc/common/_scanf.inc
+* libsrc/common/vsscanf.s
+* libsrc/conio/vcscanf.s
+
+## \_\_sin
+
+* libsrc/common/sincos.s
+
+## \_\_sys
+
+* libsrc/common/_sys.s
+* libsrc/apple2/_sys.s
+
+## \_\_sys\_oserrlist
+
+* libsrc/common/stroserr.s
+* libsrc/geos-common/system/oserrlist.s
+* libsrc/atari/oserrlist.s
+* libsrc/apple2/oserrlist.s
+* libsrc/cbm/oserrlist.s
+* libsrc/atmos/oserrlist.s
+
+## \_\_syschdir
+
+* libsrc/common/chdir.s
+* libsrc/atari/syschdir.s
+* libsrc/apple2/syschdir.s
+* libsrc/telestrat/syschdir.s
+* libsrc/cbm/syschdir.s
+
+## \_\_sysmkdir
+
+* libsrc/common/mkdir.s
+* libsrc/atari/sysmkdir.s
+* libsrc/apple2/sysmkdir.s
+* libsrc/telestrat/sysmkdir.s
+
+## \_\_sysremove
+
+* libsrc/common/remove.s
+* libsrc/geos-common/file/sysremove.s
+* libsrc/atari/sysremove.s
+* libsrc/atari/sysrmdir.s
+* libsrc/apple2/sysremove.s
+* libsrc/apple2/sysrmdir.s
+* libsrc/telestrat/sysremove.s
+* libsrc/cbm/sysremove.s
+
+## \_\_sysrename
+
+* libsrc/common/rename.s
+* libsrc/geos-common/file/sysrename.s
+* libsrc/atari/sysrename.s
+* libsrc/apple2/sysrename.s
+* libsrc/cbm/sysrename.s
+
+## \_\_sysrmdir
+
+* libsrc/common/rmdir.s
+* libsrc/atari/sysrmdir.s
+* libsrc/apple2/sysrmdir.s
+
+\_\_sysuname
+
+* libsrc/common/uname.s
+* libsrc/cbm610/sysuname.s
+* libsrc/cx16/sysuname.s
+* libsrc/plus4/sysuname.s
+* libsrc/lynx/sysuname.s
+* libsrc/c16/sysuname.s
+* libsrc/geos-common/system/sysuname.s
+* libsrc/c128/sysuname.s
+* libsrc/creativision/sysuname.s
+* libsrc/vic20/sysuname.s
+* libsrc/nes/sysuname.s
+* libsrc/atari/sysuname.s
+* libsrc/apple2/sysuname.s
+* libsrc/cbm510/sysuname.s
+* libsrc/telestrat/sysuname.s
+* libsrc/c64/sysuname.s
+* libsrc/pet/sysuname.s
+* libsrc/atari5200/sysuname.s
+* libsrc/atmos/sysuname.s
+
+# apple2
+
+## \_\_auxtype
+
+* libsrc/apple2/open.s
+
+## \_\_datetime
+
+* libsrc/apple2/open.s
+
+## \_\_dos\_type
+
+* libsrc/apple2/dioopen.s
+* libsrc/apple2/curdevice.s
+* libsrc/apple2/mainargs.s
+* libsrc/apple2/settime.s
+* libsrc/apple2/getdevice.s
+* libsrc/apple2/dosdetect.s
+* libsrc/apple2/irq.s
+* libsrc/apple2/open.s
+* libsrc/apple2/mli.s
+* libsrc/apple2/getres.s
+
+## \_\_filetype
+
+* libsrc/apple2/open.s
+* libsrc/apple2/exehdr.s
+
+## atari
+
+## \_\_defdev
+
+* libsrc/atari/posixdirent.s
+* libsrc/atari/ucase\_fn.s
+* libsrc/atari/getdefdev.s
+
+## \_\_dos\_type
+
+* libsrc/atari/getargs.s
+* libsrc/atari/exec.s
+* libsrc/atari/settime.s
+* libsrc/atari/syschdir.s
+* libsrc/atari/dosdetect.s
+* libsrc/atari/is\_cmdline\_dos.s
+* libsrc/atari/sysrmdir.s
+* libsrc/atari/gettime.s
+* libsrc/atari/lseek.s
+* libsrc/atari/getres.s
+* libsrc/atari/getdefdev.s
+
+## \_\_do\_oserror
+
+* libsrc/atari/posixdirent.s
+* libsrc/atari/do\_oserr.s
+* libsrc/atari/serref.s
+* libsrc/atari/read.s
+* libsrc/atari/write.s
+* libsrc/atari/close.s
+
+## \_\_getcolor
+
+* libsrc/atari/setcolor.s
+
+## \_\_getdefdev
+
+* libsrc/atari/getdefdev.s
+
+## \_\_graphics
+
+* libsrc/atari/graphics.s
+
+## \_\_inviocb
+
+* libsrc/atari/serref.s
+* libsrc/atari/ser/atrrdev.s
+* libsrc/atari/inviocb.s
+* libsrc/atari/read.s
+* libsrc/atari/write.s
+* libsrc/atari/lseek.s
+* libsrc/atari/close.s
+
+## \_\_is\_cmdline\_dos
+
+* libsrc/atari/is\_cmdline\_dos.s
+* libsrc/atari/doesclrscr.s
+
+## \_\_rest\_vecs
+
+* libsrc/atari/savevec.s
+
+## \_\_rwsetup
+
+* libsrc/atari/rwcommon.s
+* libsrc/atari/read.s
+* libsrc/atari/write.s
+
+## \_\_save\_vecs
+
+* libsrc/atari/savevec.s
+
+## \_\_scroll
+
+* libsrc/atari/scroll.s
+
+## \_\_setcolor
+
+* libsrc/atari/setcolor.s
+
+## \_\_setcolor\_low
+
+* libsrc/atari/setcolor.s
+
+## \_\_sio\_call
+
+* libsrc/atari/diowritev.s
+* libsrc/atari/diopncls.s
+* libsrc/atari/siocall.s
+* libsrc/atari/diowrite.s
+* libsrc/atari/dioread.s
+
+# cbm
+
+## \_\_cbm\_filetype
+
+* libsrc/cbm/cbm\_filetype.s
+* asminc/cbm\_filetype.in
+
+## \_\_dirread
+
+* libsrc/cbm/dir.inc
+* libsrc/cbm/dir.s
+
+## \_\_dirread1
+
+* libsrc/cbm/dir.inc
+* libsrc/cbm/dir.s
+
+# lynx
+
+## \_\_iodat
+
+* libsrc/lynx/lynx-cart.s
+* libsrc/lynx/bootldr.s
+* libsrc/lynx/extzp.s
+* libsrc/lynx/crt0.s
+* libsrc/lynx/extzp.inc
+
+## \_\_iodir
+
+* libsrc/lynx/extzp.s
+* libsrc/lynx/crt0.s
+* libsrc/lynx/extzp.inc
+
+## \_\_sprsys
+
+* libsrc/lynx/tgi/lynx-160-102-16.s
+* libsrc/lynx/extzp.s
+* libsrc/lynx/crt0.s
+* libsrc/lynx/extzp.inc
+
+## \_\_viddma
+
+* libsrc/lynx/tgi/lynx-160-102-16.s
+* libsrc/lynx/extzp.s
+* libsrc/lynx/crt0.s
+* libsrc/lynx/extzp.inc
+
+# pce
+
+## \_\_nmi
+
+* libsrc/pce/irq.s
+* libsrc/pce/crt0.s
diff --git a/libsrc/apple2/allow_lowercase.s b/libsrc/apple2/allow_lowercase.s
new file mode 100644
index 000000000..b78544a66
--- /dev/null
+++ b/libsrc/apple2/allow_lowercase.s
@@ -0,0 +1,28 @@
+;
+; Oliver Schmidt, 2024-08-06
+;
+; unsigned char __fastcall__ allow_lowercase (unsigned char onoff);
+;
+
+.ifndef __APPLE2ENH__
+
+ .export _allow_lowercase
+ .import return0
+ .import uppercasemask, return1
+
+_allow_lowercase:
+ tax
+ lda values,x
+ ldx uppercasemask
+ sta uppercasemask
+ cpx #$FF
+ beq :+
+ jmp return0
+: jmp return1
+
+ .rodata
+
+values: .byte $DF ; Force uppercase
+ .byte $FF ; Keep lowercase
+
+.endif
diff --git a/libsrc/apple2/beep.s b/libsrc/apple2/beep.s
new file mode 100644
index 000000000..84d132a3a
--- /dev/null
+++ b/libsrc/apple2/beep.s
@@ -0,0 +1,20 @@
+;
+; Colin Leroy-Mira, 2024
+;
+; void beep(void)
+;
+
+ .export _beep
+
+ .include "apple2.inc"
+
+ .segment "LOWCODE"
+
+_beep:
+ ; Switch in ROM and call BELL
+ bit $C082
+ jsr $FF3A ; BELL
+
+ ; Switch in LC bank 2 for R/O and return
+ bit $C080
+ rts
diff --git a/libsrc/apple2/boxchars.s b/libsrc/apple2/boxchars.s
new file mode 100644
index 000000000..8feee3bd3
--- /dev/null
+++ b/libsrc/apple2/boxchars.s
@@ -0,0 +1,73 @@
+;
+; Colin Leroy-Mira and Oliver Schmidt, 26.05.2025
+;
+; Initialize box-drawing characters according to
+; MouseText availability
+;
+
+.ifndef __APPLE2ENH__
+
+ .constructor initboxchars
+ .import machinetype
+
+ .export _CH_HLINE
+ .export _CH_VLINE
+ .export _CH_ULCORNER
+ .export _CH_URCORNER
+ .export _CH_LLCORNER
+ .export _CH_LRCORNER
+ .export _CH_TTEE
+ .export _CH_BTEE
+ .export _CH_LTEE
+ .export _CH_RTEE
+ .export _CH_CROSS
+
+ .segment "ONCE"
+
+initboxchars:
+ bit machinetype ; IIe enhanced or newer?
+ bvs out
+
+ ldx #NUM_BOXCHARS ; No mousetext, patch characters
+: lda std_boxchars,x
+ sta boxchars,x
+ dex
+ bpl :-
+
+out: rts
+
+; Replacement chars for when MouseText is not available
+std_boxchars: .byte '!'
+ .byte '-'
+ .byte '+'
+ .byte '+'
+ .byte '+'
+ .byte '+'
+
+ .data
+
+; MouseText-based box characters
+boxchars:
+VERT: .byte $DF
+HORIZ: .byte $5F
+ULCORNER: .byte $5F
+URCORNER: .byte $20
+LLCORNER: .byte $D4
+LRCORNER: .byte $DF
+
+NUM_BOXCHARS = *-boxchars
+
+; exported symbols, referencing our 6 bytes
+_CH_HLINE = HORIZ
+_CH_VLINE = VERT
+_CH_ULCORNER = ULCORNER
+_CH_URCORNER = URCORNER
+_CH_LLCORNER = LLCORNER
+_CH_LRCORNER = LRCORNER
+_CH_TTEE = ULCORNER
+_CH_BTEE = LLCORNER
+_CH_LTEE = LLCORNER
+_CH_RTEE = LRCORNER
+_CH_CROSS = LLCORNER
+
+.endif ; not __APPLE2ENH__
diff --git a/libsrc/apple2/callmain.s b/libsrc/apple2/callmain.s
new file mode 100644
index 000000000..71a8b5611
--- /dev/null
+++ b/libsrc/apple2/callmain.s
@@ -0,0 +1,75 @@
+;
+; Ullrich von Bassewitz, 2003-03-07
+;
+; Push arguments and call main()
+;
+
+
+ .export callmain, _exit
+ .export __argc, __argv
+
+ .import _main, pushax, done, donelib
+ .import zpsave, rvsave, reset
+
+ .include "zeropage.inc"
+ .include "apple2.inc"
+
+
+;---------------------------------------------------------------------------
+; Setup the stack for main(), then jump to it
+
+callmain:
+ lda __argc
+ ldx __argc+1
+ jsr pushax ; Push argc
+
+ lda __argv
+ ldx __argv+1
+ jsr pushax ; Push argv
+
+ ldy #4 ; Argument size
+ jsr _main
+
+ ; Avoid a re-entrance of donelib. This is also the exit() entry.
+_exit: ldx #exit
+ jsr reset ; Setup RESET vector
+
+ ; 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
+ sta SOFTEV,x
+ dex
+ bpl :-
+
+ ; Copy back the zero-page stuff.
+ ldx #zpspace-1
+: lda zpsave,x
+ sta sp,x
+ dex
+ bpl :-
+
+ ; ProDOS TechRefMan, chapter 5.2.1:
+ ; "System programs should set the stack pointer to $FF at the
+ ; warm-start entry point."
+ ldx #$FF
+ txs ; Re-init stack pointer
+
+ ; We're done
+ jmp done
+
+;---------------------------------------------------------------------------
+; Data
+
+.data
+__argc: .word 0
+__argv: .addr 0
diff --git a/libsrc/apple2/cgetc.s b/libsrc/apple2/cgetc.s
index ecce9f9de..f0b9566ff 100644
--- a/libsrc/apple2/cgetc.s
+++ b/libsrc/apple2/cgetc.s
@@ -7,8 +7,13 @@
;
.export _cgetc
+
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
.import cursor, putchardirect
+ .include "zeropage.inc"
.include "apple2.inc"
_cgetc:
@@ -17,12 +22,15 @@ _cgetc:
beq :+
; Show caret.
- .ifdef __APPLE2ENH__
- lda #$7F | $80 ; Checkerboard, screen code
- .else
+ .ifndef __APPLE2ENH__
lda #' ' | $40 ; Blank, flashing
+ bit machinetype
+ bpl put_caret
.endif
- jsr putchardirect ; Returns old character in X
+
+ lda #$7F | $80 ; Checkerboard, screen code
+put_caret:
+ jsr putchardirect ; Saves old character in tmp3
; Wait for keyboard strobe.
: inc RNDL ; Increment random counter low
@@ -37,16 +45,20 @@ _cgetc:
; Restore old character.
pha
- txa
+ lda tmp3
jsr putchardirect
pla
; At this time, the high bit of the key pressed is set.
: bit KBDSTRB ; Clear keyboard strobe
- .ifdef __APPLE2ENH__
+
+ .ifndef __APPLE2ENH__
+ bit machinetype ; Apple //e or more recent?
+ bpl clear
+ .endif
bit BUTN0 ; Check if OpenApple is down
bmi done
- .endif
- and #$7F ; If not down, then clear high bit
+
+clear: and #$7F ; If not down, then clear high bit
done: ldx #>$0000
rts
diff --git a/libsrc/apple2/closedir.c b/libsrc/apple2/closedir.c
deleted file mode 100644
index d37d15bba..000000000
--- a/libsrc/apple2/closedir.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*****************************************************************************/
-/* */
-/* closedir.c */
-/* */
-/* Close a directory */
-/* */
-/* */
-/* */
-/* (C) 2005 Oliver Schmidt, */
-/* */
-/* */
-/* 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
-#include
-#include
-#include "dir.h"
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-int __fastcall__ closedir (DIR* dir)
-{
- int result;
-
- /* Cleanup directory file */
- result = close (dir->fd);
-
- /* Cleanup DIR */
- free (dir);
-
- return result;
-}
diff --git a/libsrc/apple2/closedir.s b/libsrc/apple2/closedir.s
new file mode 100644
index 000000000..1f176092b
--- /dev/null
+++ b/libsrc/apple2/closedir.s
@@ -0,0 +1,35 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; int __fastcall__ closedir (DIR *dir)
+;
+
+ .export _closedir, closedir_ptr1
+
+ .import _close
+ .import _free
+ .import pushax, popax, pushptr1, swapstk
+
+ .importzp ptr1
+
+ .include "apple2.inc"
+ .include "dir.inc"
+ .include "errno.inc"
+ .include "fcntl.inc"
+ .include "zeropage.inc"
+
+_closedir:
+ sta ptr1
+ stx ptr1+1
+closedir_ptr1:
+ ; Close fd
+ jsr pushptr1 ; Backup ptr1
+ ldy #$00
+ lda (ptr1),y ; Get fd
+ ldx #$00
+ jsr _close
+ jsr swapstk ; Store result, pop ptr1
+
+ ; Free dir structure
+ jsr _free
+ jmp popax ; Return result
diff --git a/libsrc/apple2/cpeekc.s b/libsrc/apple2/cpeekc.s
index a547f7867..4f5361461 100644
--- a/libsrc/apple2/cpeekc.s
+++ b/libsrc/apple2/cpeekc.s
@@ -4,25 +4,39 @@
; char cpeekc (void);
;
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
+
.export _cpeekc
.include "apple2.inc"
_cpeekc:
ldy CH
- .ifdef __APPLE2ENH__
+
+ sec ; Assume main memory
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl peek
+ .endif
+
bit RD80VID ; In 80 column mode?
bpl peek ; No, just go ahead
- tya
+ lda OURCH
lsr ; Div by 2
tay
bcs peek ; Odd cols are in main memory
+ php
+ sei ; No valid MSLOT et al. in aux memory
bit HISCR ; Assume SET80COL
- .endif
+
peek: lda (BASL),Y ; Get character
- .ifdef __APPLE2ENH__
- bit LOWSCR ; Doesn't hurt in 40 column mode
- .endif
- eor #$80 ; Invert high bit
- ldx #$00
+ bcs :+ ; In main memory
+ bit LOWSCR
+ plp
+
+: eor #$80 ; Invert high bit
+ ldx #>$0000
rts
diff --git a/libsrc/apple2/cputc.s b/libsrc/apple2/cputc.s
index 035b1c047..aa4a383b3 100644
--- a/libsrc/apple2/cputc.s
+++ b/libsrc/apple2/cputc.s
@@ -5,25 +5,33 @@
; void __fastcall__ cputc (char c);
;
- .ifdef __APPLE2ENH__
.constructor initconio
- .endif
.export _cputcxy, _cputc
.export cputdirect, newline, putchar, putchardirect
.import gotoxy, VTABZ
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .import uppercasemask
+ .endif
+
+ .include "zeropage.inc"
.include "apple2.inc"
.macpack cpu
.segment "ONCE"
- .ifdef __APPLE2ENH__
initconio:
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bmi :+
+ rts
+:
+ .endif
sta SETALTCHAR ; Switch in alternate charset
bit LORES ; Limit SET80COL-HISCR to text
rts
- .endif
.code
@@ -35,7 +43,7 @@ _cputcxy:
pla ; Restore C and run into _cputc
_cputc:
- cmp #$0D ; Test for \r = carrage return
+ cmp #$0D ; Test for \r = carriage return
beq left
cmp #$0A ; Test for \n = line feed
beq newline
@@ -43,24 +51,53 @@ _cputc:
.ifndef __APPLE2ENH__
cmp #$E0 ; Test for lowercase
bcc cputdirect
- and #$DF ; Convert to uppercase
+ and uppercasemask
.endif
cputdirect:
jsr putchar
- inc CH ; Bump to next column
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl :+
+ .endif
+ bit RD80VID ; In 80 column mode?
+ bpl :+
+ inc OURCH ; Bump to next column
+ lda OURCH
+ .ifdef __APPLE2ENH__
+ bra check ; Must leave CH alone
+ .else
+ jmp check
+ .endif
+
+: inc CH ; Bump to next column
lda CH
- cmp WNDWDTH
- bcc :+
+check: cmp WNDWDTH
+ bcc done
jsr newline
left:
- .if (.cpu .bitand CPU_ISET_65SC02)
+ .ifdef __APPLE2ENH__
stz CH ; Goto left edge of screen
.else
- lda #$00 ; Goto left edge of screen
+ lda #$00
sta CH
.endif
-: rts
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl done
+ .endif
+
+ bit RD80VID ; In 80 column mode?
+ bpl done
+ .ifdef __APPLE2ENH__
+ stz OURCH ; Goto left edge of screen
+ .else
+ sta OURCH
+ .endif
+
+done: rts
newline:
inc CV ; Bump to next line
@@ -84,23 +121,32 @@ putchar:
mask: and INVFLG ; Apply normal, inverse, flash
putchardirect:
- pha
- .ifdef __APPLE2ENH__
- lda CH
+ tax
+ ldy CH
+
+ sec ; Assume main memory
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl put
+ .endif
+
bit RD80VID ; In 80 column mode?
bpl put ; No, just go ahead
+ lda OURCH
lsr ; Div by 2
+ tay
bcs put ; Odd cols go in main memory
+ php
+ sei ; No valid MSLOT et al. in aux memory
bit HISCR ; Assume SET80COL
-put: tay
- .else
- ldy CH
- .endif
- lda (BASL),Y ; Get current character
- tax ; Return old character for _cgetc
- pla
+
+put: lda (BASL),Y ; Get current character
+ sta tmp3 ; Save old character for _cgetc
+ txa
sta (BASL),Y
- .ifdef __APPLE2ENH__
- bit LOWSCR ; Doesn't hurt in 40 column mode
- .endif
- rts
+
+ bcs :+ ; In main memory
+ bit LOWSCR
+ plp
+: rts
diff --git a/libsrc/apple2/crt0.s b/libsrc/apple2/crt0.s
index 60a8516d1..628303687 100644
--- a/libsrc/apple2/crt0.s
+++ b/libsrc/apple2/crt0.s
@@ -4,11 +4,13 @@
; Startup code for cc65 (Apple2 version)
;
- .export _exit, done, return
+ .export done, return
+ .export zpsave, rvsave, reset
.export __STARTUP__ : absolute = 1 ; Mark as startup
- .import initlib, donelib
+ .import initlib, _exit
.import zerobss, callmain
+ .import bltu2
.import __ONCE_LOAD__, __ONCE_SIZE__ ; Linker generated
.import __LC_START__, __LC_LAST__ ; Linker generated
@@ -33,41 +35,7 @@
jsr zerobss
; Push the command-line arguments; and, call main().
- jsr callmain
-
- ; Avoid a re-entrance of donelib. This is also the exit() entry.
-_exit: ldx #exit
- jsr reset ; Setup RESET vector
-
- ; Switch in ROM, in case it wasn't already switched in by a RESET.
- bit $C082
-
- ; Call the module destructors.
- jsr donelib
-
- ; Restore the original RESET vector.
-exit: ldx #$02
-: lda rvsave,x
- sta SOFTEV,x
- dex
- bpl :-
-
- ; Copy back the zero-page stuff.
- ldx #zpspace-1
-: lda zpsave,x
- sta sp,x
- dex
- bpl :-
-
- ; ProDOS TechRefMan, chapter 5.2.1:
- ; "System programs should set the stack pointer to $FF at the
- ; warm-start entry point."
- ldx #$FF
- txs ; Re-init stack pointer
-
- ; We're done
- jmp done
+ jmp callmain
; ------------------------------------------------------------------------
@@ -126,6 +94,7 @@ basic: lda HIMEM
; Call the module constructors.
jsr initlib
+ ; Copy the LC segment to its destination
; Switch in LC bank 2 for W/O.
bit $C081
bit $C081
@@ -153,7 +122,7 @@ basic: lda HIMEM
; Call into Applesoft Block Transfer Up -- which handles zero-
; sized blocks well -- to move the content of the LC memory area.
- jsr $D39A ; BLTU2
+ jsr bltu2
; Switch in LC bank 2 for R/O and return.
bit $C080
diff --git a/libsrc/apple2/curdevice.s b/libsrc/apple2/curdevice.s
index 9781b8ad0..a450c605f 100644
--- a/libsrc/apple2/curdevice.s
+++ b/libsrc/apple2/curdevice.s
@@ -23,5 +23,5 @@ _getcurrentdevice:
bne :+
lda #$FF ; INVALID_DEVICE
-: ldx #$00
+: ldx #>$0000
rts
diff --git a/libsrc/apple2/detect80cols.s b/libsrc/apple2/detect80cols.s
new file mode 100644
index 000000000..c6f263566
--- /dev/null
+++ b/libsrc/apple2/detect80cols.s
@@ -0,0 +1,56 @@
+;
+; Colin Leroy-Mira, 27/05/2025
+;
+; Verify the presence of a 80 columns card in slot 3,
+; and publish a flag accordingly.
+;
+ .export aux80col
+
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
+
+ .constructor detect80cols
+
+ .include "apple2.inc"
+
+ .data
+
+aux80col: .byte 0
+
+ .segment "ONCE"
+
+IdOfsTable: ; Table of bytes positions, used to check four
+ ; specific bytes on the slot's firmware to make
+ ; sure this is a serial card.
+ .byte $05 ; Pascal 1.0 ID byte
+ .byte $07 ; Pascal 1.0 ID byte
+ .byte $0B ; Pascal 1.1 generic signature byte
+ .byte $0C ; Device signature byte
+
+IdValTable: ; Table of expected values for the four checked
+ ; bytes
+ .byte $38 ; ID Byte 0 (from Pascal 1.0), fixed
+ .byte $18 ; ID Byte 1 (from Pascal 1.0), fixed
+ .byte $01 ; Generic signature for Pascal 1.1, fixed
+ .byte $88 ; Device signature byte (80 columns card)
+
+IdTableLen = * - IdValTable
+
+detect80cols:
+ .ifndef __APPLE2ENH__
+ bit machinetype ; Check we're on a //e at least, otherwise we
+ bpl NoDev ; handle no 80cols hardware (like Videx)
+ .endif
+
+ ldx #IdTableLen-1
+: ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes
+ lda IdValTable,x
+ cmp $C300,y
+ bne NoDev
+ dex
+ bpl :-
+
+ dec aux80col ; We have an 80-columns card! Set flag to $FF
+
+NoDev: rts
diff --git a/libsrc/apple2/detect_iigs.s b/libsrc/apple2/detect_iigs.s
new file mode 100644
index 000000000..f82a464ac
--- /dev/null
+++ b/libsrc/apple2/detect_iigs.s
@@ -0,0 +1,17 @@
+;
+; Colin Leroy-Mira , 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
diff --git a/libsrc/apple2/diocommon.s b/libsrc/apple2/diocommon.s
index b18f0e6ef..6870ebb51 100644
--- a/libsrc/apple2/diocommon.s
+++ b/libsrc/apple2/diocommon.s
@@ -31,5 +31,5 @@ diocommon:
dioepilog:
; Return success or error
sta ___oserror
- ldx #$00
+ ldx #>$0000
rts
diff --git a/libsrc/apple2/dir.h b/libsrc/apple2/dir.h
deleted file mode 100644
index 369080c47..000000000
--- a/libsrc/apple2/dir.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*****************************************************************************/
-/* */
-/* dir.h */
-/* */
-/* Apple ][ system specific DIR */
-/* */
-/* */
-/* */
-/* (C) 2005 Oliver Schmidt, */
-/* */
-/* */
-/* 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. */
-/* */
-/*****************************************************************************/
-
-
-
-#ifndef _DIR_H
-#define _DIR_H
-
-
-
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-struct DIR {
- int fd;
- unsigned char entry_length;
- unsigned char entries_per_block;
- unsigned char current_entry;
- union {
- unsigned char bytes[512];
- struct {
- unsigned prev_block;
- unsigned next_block;
- unsigned char entries[1];
- } content;
- } block;
-};
-
-
-
-/* End of dir.h */
-#endif
diff --git a/libsrc/apple2/dir.inc b/libsrc/apple2/dir.inc
new file mode 100644
index 000000000..545ae003b
--- /dev/null
+++ b/libsrc/apple2/dir.inc
@@ -0,0 +1,15 @@
+.struct DIR
+ FD .word
+ ENTRY_LENGTH .byte
+ ENTRIES_PER_BLOCK .byte
+ FILE_COUNT .word
+ CURRENT_ENTRY .byte
+ .union
+ BYTES .byte 512
+ .struct CONTENT
+ PREV_BLOCK .word
+ NEXT_BLOCK .word
+ ENTRIES .byte
+ .endstruct
+ .endunion
+.endstruct
diff --git a/libsrc/apple2/dir_entry_count.s b/libsrc/apple2/dir_entry_count.s
new file mode 100644
index 000000000..6a80bef28
--- /dev/null
+++ b/libsrc/apple2/dir_entry_count.s
@@ -0,0 +1,24 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; unsigned int __fastcall__ dir_entry_count(DIR *dir);
+;
+
+ .export _dir_entry_count
+
+ .importzp ptr1
+
+ .include "apple2.inc"
+ .include "dir.inc"
+
+.proc _dir_entry_count
+ sta ptr1
+ stx ptr1+1
+
+ ldy #DIR::FILE_COUNT + 1
+ lda (ptr1),y
+ tax
+ dey
+ lda (ptr1),y
+ rts
+.endproc
diff --git a/libsrc/apple2/dynchline.s b/libsrc/apple2/dynchline.s
new file mode 100644
index 000000000..74cc5a41e
--- /dev/null
+++ b/libsrc/apple2/dynchline.s
@@ -0,0 +1,41 @@
+;
+; Ullrich von Bassewitz, 08.08.1998
+; Colin Leroy-Mira, 26.05.2025
+;
+; void __fastcall__ dyn_chlinexy (unsigned char c, unsigned char x, unsigned char y, unsigned char length);
+; void __fastcall__ dyn_chline (unsigned char c, unsigned char length);
+;
+
+.ifndef __APPLE2ENH__
+
+ .export _dyn_chlinexy, _dyn_chline, chlinedirect
+ .import gotoxy, cputdirect, popa
+ .import machinetype
+
+ .include "zeropage.inc"
+ .include "apple2.inc"
+
+_dyn_chlinexy:
+ pha ; Save the length
+ jsr gotoxy ; Call this one, will pop params
+ pla ; Restore the length and run into _chline
+
+_dyn_chline:
+ pha
+ jsr popa ; Get the character to draw
+ eor #$80 ; Invert high bit
+ tax
+ pla
+
+chlinedirect:
+ stx tmp1
+ cmp #$00 ; Is the length zero?
+ beq done ; Jump if done
+ sta tmp2
+: lda tmp1 ; Screen code
+ jsr cputdirect ; Direct output
+ dec tmp2
+ bne :-
+done: rts
+
+.endif
diff --git a/libsrc/apple2/dyncvline.s b/libsrc/apple2/dyncvline.s
new file mode 100644
index 000000000..b74126a4d
--- /dev/null
+++ b/libsrc/apple2/dyncvline.s
@@ -0,0 +1,40 @@
+;
+; Ullrich von Bassewitz, 08.08.1998
+; Colin Leroy-Mira, 26.05.2025
+;
+; void __fastcall__ dyn_cvlinexy (unsigned char c, unsigned char x, unsigned char y, unsigned char length);
+; void __fastcall__ dyn_cvline (unsigned char c, unsigned char length);
+;
+
+.ifndef __APPLE2ENH__
+
+ .export _dyn_cvlinexy, _dyn_cvline
+ .import gotoxy, putchar, newline, popa
+ .import machinetype
+
+ .include "zeropage.inc"
+
+_dyn_cvlinexy:
+ pha ; Save the length
+ jsr gotoxy ; Call this one, will pop params
+ pla ; Restore the length and run into _cvline
+
+_dyn_cvline:
+ pha
+ jsr popa ; Get the character to draw
+ eor #$80 ; Invert high bit
+ tax
+ pla
+
+ stx tmp1
+ cmp #$00 ; Is the length zero?
+ beq done ; Jump if done
+ sta tmp2
+: lda tmp1 ; Screen code
+ jsr putchar ; Write, no cursor advance
+ jsr newline ; Advance cursor to next line
+ dec tmp2
+ bne :-
+done: rts
+
+.endif
diff --git a/libsrc/apple2/exec.s b/libsrc/apple2/exec.s
index 27a6487bd..4e5e77a6e 100644
--- a/libsrc/apple2/exec.s
+++ b/libsrc/apple2/exec.s
@@ -5,8 +5,9 @@
;
.export _exec
- .import pushname, popname
- .import popax, done, _exit
+ .import mli_file_info_direct
+ .import aux80col
+ .import pushname, popname, popax, done, _exit
.include "zeropage.inc"
.include "errno.inc"
@@ -17,13 +18,12 @@
typerr: lda #$4A ; "Incompatible file format"
; Cleanup name
-oserr: jsr popname ; Preserves A
- ; Set ___oserror
- jmp ___mappederrno
+mlierr: jsr popname
+oserr: jmp ___mappederrno
_exec:
- ; Save cmdline
+ ; Store cmdline
sta ptr4
stx ptr4+1
@@ -32,6 +32,9 @@ _exec:
jsr pushname
bne oserr
+ jsr mli_file_info_direct
+ bcs mlierr
+
; ProDOS TechRefMan, chapter 5.1.5.1:
; "The complete or partial pathname of the system program
; is stored at $280, starting with a length byte."
@@ -46,18 +49,6 @@ _exec:
dey
bpl :-
- ; Set pushed name
- lda sp
- ldx sp+1
- sta mliparam + MLI::INFO::PATHNAME
- stx mliparam + MLI::INFO::PATHNAME+1
-
- ; Get file_type and aux_type
- lda #GET_INFO_CALL
- ldx #GET_INFO_COUNT
- jsr callmli
- bcs oserr
-
; If we get here the program file at least exists so we copy
; the loader stub right now and patch it later to set params
ldx #size - 1
@@ -121,37 +112,12 @@ setbuf: lda #$00 ; Low byte
dex
dex
- ; Set I/O buffer
- sta mliparam + MLI::OPEN::IO_BUFFER
- stx mliparam + MLI::OPEN::IO_BUFFER+1
+ ; Set OPEN MLI call I/O buffer parameter
+ sta io_buffer
+ stx io_buffer+1
- ; PATHNAME already set
- .assert MLI::OPEN::PATHNAME = MLI::INFO::PATHNAME, error
-
- ; Lower file level to avoid program file
- ; being closed by C library shutdown code
- ldx LEVEL
- stx level
- beq :+
- dec LEVEL
-
- ; Open file
-: lda #OPEN_CALL
- ldx #OPEN_COUNT
- jsr callmli
-
- ; Restore file level
- ldx level
- stx LEVEL
- bcc :+
- jmp oserr
-
- ; Get and save fd
-: lda mliparam + MLI::OPEN::REF_NUM
- sta read_ref
- sta close_ref
-
- .ifdef __APPLE2ENH__
+ bit aux80col
+ bpl :+
; Calling the 80 column firmware needs the ROM switched
; in, otherwise it copies the F8 ROM to the LC (@ $CEF4)
bit $C082
@@ -164,9 +130,8 @@ setbuf: lda #$00 ; Low byte
; Switch in LC bank 2 for R/O
bit $C080
- .endif
- ; Reset stack as we already passed
+: ; Reset stack as we already passed
; the point of no return anyway
ldx #$FF
txs
@@ -194,14 +159,25 @@ setbuf: lda #$00 ; Low byte
; Initiate C library shutdown
jmp _exit
- .bss
-
-level : .res 1
-
.rodata
+source:
+ ; Open program file
+ ; PATHNAME parameter is already set (we reuse
+ ; the copy at $0280); IO_BUFFER has been setup
+ ; before shutting down the C library
+ jsr $BF00
+ .byte OPEN_CALL
+ .word open_param
+ bcs error
+
+ ; Copy REF_NUM to MLI READ and CLOSE parameters
+ lda open_ref
+ sta read_ref
+ sta close_ref
+
; Read whole program file
-source: jsr $BF00
+ jsr $BF00
.byte READ_CALL
.word read_param
bcs error
@@ -254,6 +230,14 @@ jump: jmp (data_buffer)
file_type = * - source + target
.byte $00
+open_param = * - source + target
+ .byte $03 ; PARAM_COUNT
+ .addr $0280 ; PATHNAME
+io_buffer = * - source + target
+ .addr $0000 ; IO_BUFFER
+open_ref = * - source + target
+ .byte $00 ; REF_NUM
+
read_param = * - source + target
.byte $04 ; PARAM_COUNT
read_ref = * - source + target
@@ -285,4 +269,8 @@ size = * - source
target = DOSWARM - size
+ ; Make sure that the loader isn't too big, and
+ ; fits in $300-$3D0
+ .assert target >= $300, error
+
dosvec: jmp quit
diff --git a/libsrc/apple2/extra/integer-basic-compat.s b/libsrc/apple2/extra/integer-basic-compat.s
new file mode 100644
index 000000000..c4dc8d8ef
--- /dev/null
+++ b/libsrc/apple2/extra/integer-basic-compat.s
@@ -0,0 +1,33 @@
+;
+; Colin Leroy-Mira, 06.03.2025
+;
+; Copy the LC segment from the end of the binary to the Language Card
+; using _memcpy. This allows running apple2 programs on the original
+; Integer ROM Apple ][.
+;
+
+ .export bltu2
+
+ .import _memcpy, pushax
+ .import __ONCE_LOAD__, __ONCE_SIZE__ ; Linker generated
+ .import __LC_START__, __LC_LAST__ ; Linker generated
+
+ .segment "ONCE"
+
+bltu2:
+ ; Get the destination start address.
+ lda #<__LC_START__
+ ldx #>__LC_START__
+ jsr pushax
+
+ ; Get the source start address.
+ lda #<(__ONCE_LOAD__ + __ONCE_SIZE__)
+ ldx #>(__ONCE_LOAD__ + __ONCE_SIZE__)
+ jsr pushax
+
+ ; Set the length
+ lda #<(__LC_LAST__ - __LC_START__)
+ ldx #>(__LC_LAST__ - __LC_START__)
+
+ ; And do the copy
+ jmp _memcpy
diff --git a/libsrc/apple2/extra/iobuf-0800.s b/libsrc/apple2/extra/iobuf-0800.s
index 694b91fdb..b00dba6ae 100644
--- a/libsrc/apple2/extra/iobuf-0800.s
+++ b/libsrc/apple2/extra/iobuf-0800.s
@@ -54,18 +54,20 @@ iobuf_alloc:
rts
; Mark table entry as used
-: lda #$FF
- sta table,x
+: dec table,x
; Convert table index to address hibyte
txa
asl
asl
- clc
+ ; Skip clearing carry, it can't be set as long as MAX_FDS*4 is
+ ; less than 64.
+ .assert MAX_FDS*4 < $40, error
adc #>$0800
; Store address in "memptr"
- ldy #$01
+ ; (Y still equals 0 from popptr1)
+ iny
sta (ptr1),y
dey
tya
@@ -82,8 +84,7 @@ iobuf_free:
; Mark table entry as free
tax
- lda #$00
- sta table,x
+ inc table,x
rts
; ------------------------------------------------------------------------
diff --git a/libsrc/apple2/filename.s b/libsrc/apple2/filename.s
index aaef6ec2d..0d4b6bedd 100644
--- a/libsrc/apple2/filename.s
+++ b/libsrc/apple2/filename.s
@@ -8,6 +8,7 @@
.import subysp, addysp, decsp1
.include "zeropage.inc"
+ .include "apple2.inc"
.include "mli.inc"
pushname:
@@ -15,7 +16,7 @@ pushname:
stx ptr1+1
; Alloc pathname buffer
- ldy #64+1 ; Max pathname length + zero
+ ldy #FILENAME_MAX
jsr subysp
; Check for full pathname
@@ -71,14 +72,14 @@ copy: lda (ptr1),y
sta (sp),y
beq setlen
iny
- cpy #64+1 ; Max pathname length + zero
+ cpy #FILENAME_MAX
bcc copy
; Load oserror code
lda #$40 ; "Invalid pathname"
; Free pathname buffer
-addsp65:ldy #64+1
+addsp65:ldy #FILENAME_MAX
bne addsp ; Branch always
; Alloc and set length byte
@@ -93,5 +94,5 @@ setlen: tya
popname:
; Cleanup stack
- ldy #1 + 64+1 ; Length byte + max pathname length + zero
-addsp: jmp addysp ; Preserves A
+ ldy #1 + FILENAME_MAX
+addsp: jmp addysp ; Preserves A and X
diff --git a/libsrc/apple2/get_iigs_speed.s b/libsrc/apple2/get_iigs_speed.s
new file mode 100644
index 000000000..b960516fa
--- /dev/null
+++ b/libsrc/apple2/get_iigs_speed.s
@@ -0,0 +1,22 @@
+;
+; Colin Leroy-Mira , 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 #>$0000
+ rts
+ .assert SPEED_SLOW = 0, error
+: jmp return0 ; SPEED_SLOW
diff --git a/libsrc/apple2/get_ostype.s b/libsrc/apple2/get_ostype.s
index a1b1eb5be..0bd53717c 100644
--- a/libsrc/apple2/get_ostype.s
+++ b/libsrc/apple2/get_ostype.s
@@ -4,8 +4,10 @@
; unsigned char get_ostype (void)
;
+ ; Priority higher than the default one so that things depending
+ ; on ostype can get ostype set when called at normal priority
.constructor initostype, 9
- .export _get_ostype
+ .export _get_ostype, ostype
; Identify machine according to:
; Apple II Miscellaneous TechNote #7, Apple II Family Identification
diff --git a/libsrc/apple2/get_tv.s b/libsrc/apple2/get_tv.s
new file mode 100644
index 000000000..b2eb4d857
--- /dev/null
+++ b/libsrc/apple2/get_tv.s
@@ -0,0 +1,189 @@
+;
+; Colin Leroy-Mira , 2025
+;
+; unsigned char __fastcall__ get_tv(void)
+;
+ .export _get_tv
+
+ .import _set_iigs_speed, _get_iigs_speed
+ .import ostype
+
+ .constructor calibrate_tv, 8 ; After ostype
+
+ .include "accelerator.inc"
+ .include "apple2.inc"
+ .include "get_tv.inc"
+
+ .segment "ONCE"
+
+; Cycle wasters
+waste_72:
+ jsr waste_36
+waste_36:
+ jsr waste_12
+waste_24:
+ jsr waste_12
+waste_12:
+ rts
+
+.proc calibrate_tv
+ lda ostype
+ bmi iigs
+ cmp #$20
+ bcc iip
+ cmp #$40
+ bcc iie
+
+iic: jmp calibrate_iic
+iigs: jmp calibrate_iigs
+iie: jmp calibrate_iie
+iip: rts ; Keep TV::OTHER.
+.endproc
+
+
+; Magic numbers
+WASTE_LOOP_CYCLES = 92 ; The wait loop total cycles
+NTSC_LOOP_COUNT = 17030/WASTE_LOOP_CYCLES ; How many loops expected on NTSC
+PAL_LOOP_COUNT = 20280/WASTE_LOOP_CYCLES ; How many loops expected on PAL
+STOP_PTRIG = 16500/WASTE_LOOP_CYCLES ; Stop PTRIG at 16.5ms
+
+; Carry set at enter: wait for VBL +
+; Carry clear at enter: wait for VBL -
+; Increments X every 92 cycles.
+.proc count_until_vbl_bit
+ lda #$10 ; BPL
+ bcc :+
+ lda #$30 ; BMI
+: sta sign
+
+ ; Wait for VBLsign change with 92 cycles loops.
+ ; Hit PTRIG repeatedly so that accelerators will slow down.
+ ; But stop hitting PTRIG after 16.5ms cycles, so that on the //c,
+ ; the VBLINT will not be reset right before we get it. 16.5ms
+ ; is a good value because it's far enough from 17ms for NTSC
+ ; models, and close enough to 20.2ms for PAL models that accelerators
+ ; will stay slow until there. (5ms usually).
+
+: cpx #STOP_PTRIG ; 2 - see if we spent 16.5ms already
+ bcs notrig ; 4 / 5 - if so, stop hitting PTRIG
+ sta PTRIG ; 8 - otherwise hit it
+ bcc count ; 11
+notrig:
+ nop ; 7 - keep cycle count constant when not
+ nop ; 9 - hitting PTRIG
+ nop ; 11
+count:
+ inx ; 13
+ jsr waste_72 ; 85
+ bit RDVBLBAR ; 89 - Wait for VBL change
+sign:
+ bpl :- ; 92 - patched with bpl/bmi
+ rts
+.endproc
+
+.proc calibrate_iic
+ php
+ sei
+
+ sta IOUDISOFF
+ lda RDVBLMSK
+ pha ; Back up for cleanup
+
+ bit ENVBL
+ bit PTRIG ; Reset VBL interrupt flag
+: bit RDVBLBAR ; Wait for one VBL
+ bpl :-
+
+ bit PTRIG ; Reset VBL interrupt flag again
+ ldx #$00
+ clc
+ jsr count_until_vbl_bit
+
+ pla ; Cleanup
+ asl
+ bcs :+ ; VBL interrupts were already enabled
+ bit DISVBL
+: sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on.
+
+ plp
+ jmp calibrate_done
+.endproc
+
+.proc calibrate_iie
+: bit RDVBLBAR ; Wait for bit 7 to be off (VBL start)
+ bmi :-
+: bit RDVBLBAR ; Wait for bit 7 to be on (VBL end)
+ bpl :-
+
+ ; Wait and count during a full cycle
+ ldx #$00
+ sec
+ jsr count_until_vbl_bit
+ clc
+ jsr count_until_vbl_bit
+
+ jmp calibrate_done
+.endproc
+
+.proc calibrate_iigs
+ ; Backup speed and slow down
+ jsr _get_iigs_speed
+ pha
+ lda #SPEED_SLOW
+ jsr _set_iigs_speed
+
+ ; The same as IIe, but reverted, because... something?
+: bit RDVBLBAR ; Wait for bit 7 to be on (VBL start)
+ bpl :-
+: bit RDVBLBAR ; Wait for bit 7 to be off (VBL end)
+ bmi :-
+
+ ; Wait and count during a full cycle
+ ldx #$00
+ clc
+ jsr count_until_vbl_bit
+ sec
+ jsr count_until_vbl_bit
+
+ jsr calibrate_done
+
+ ; Restore user speed
+ pla
+ jmp _set_iigs_speed
+.endproc
+
+.proc calibrate_done
+ ; Consider X +/- 3 to be valid,
+ ; anything else is unknown.
+
+ lda #TV::NTSC
+ cpx #NTSC_LOOP_COUNT-3
+ bcc unexpected
+ cpx #NTSC_LOOP_COUNT+3
+ bcc matched
+
+ lda #TV::PAL
+ cpx #PAL_LOOP_COUNT-3
+ bcc unexpected
+ cpx #PAL_LOOP_COUNT+3
+ bcs unexpected
+
+matched:
+ sta tv
+
+unexpected:
+ rts
+.endproc
+
+ .code
+
+; The only thing remaining from that code after init
+.proc _get_tv
+ lda tv
+ ldx #>$0000
+ rts
+.endproc
+
+ .segment "INIT"
+
+tv: .byte TV::OTHER
diff --git a/libsrc/apple2/getdevice.s b/libsrc/apple2/getdevice.s
index 0c674cad0..f3b0d5a86 100644
--- a/libsrc/apple2/getdevice.s
+++ b/libsrc/apple2/getdevice.s
@@ -30,5 +30,5 @@ next: inx
bne next
done: txa
- ldx #$00
+ ldx #>$0000
rts
diff --git a/libsrc/apple2/gettime.s b/libsrc/apple2/gettime.s
index 7467b0189..829aaab3b 100644
--- a/libsrc/apple2/gettime.s
+++ b/libsrc/apple2/gettime.s
@@ -4,7 +4,8 @@
; int __fastcall__ clock_gettime (clockid_t clk_id, struct timespec *tp);
;
- .import pushax, steaxspidx, incsp1, incsp3, return0
+ .import pushax, incsp1, incsp3, steaxspidx, return0
+ .import _mktime_dt
.include "time.inc"
.include "zeropage.inc"
@@ -29,42 +30,12 @@ _clock_gettime:
jsr callmli
bcs oserr
- ; Get date
- lda DATELO+1
- lsr
- php ; Save month msb
- cmp #70 ; Year < 70?
- bcs :+ ; No, leave alone
- adc #100 ; Move 19xx to 20xx
-: sta TM + tm::tm_year
- lda DATELO
- tax ; Save day
- plp ; Restore month msb
- ror
- lsr
- lsr
- lsr
- lsr
- beq erange ; [1..12] allows for validity check
- tay
- dey ; Move [1..12] to [0..11]
- sty TM + tm::tm_mon
- txa ; Restore day
- and #%00011111
- sta TM + tm::tm_mday
+ ; Convert DATELO/TIMELO to time_t
+ lda #DATELO
+ jsr _mktime_dt
- ; Get time
- lda TIMELO+1
- sta TM + tm::tm_hour
- lda TIMELO
- sta TM + tm::tm_min
-
- ; Make time_t
- lda #TM
- jsr _mktime
-
- ; Store tv_sec
+ ; Store
ldy #timespec::tv_sec
jsr steaxspidx
@@ -74,21 +45,8 @@ _clock_gettime:
; Return success
jmp return0
- ; Load errno code
-erange: lda #ERANGE
-
- ; Cleanup stack
- jsr incsp3 ; Preserves A
-
- ; Set __errno
- jmp ___directerrno
-
; Cleanup stack
oserr: jsr incsp3 ; Preserves A
; Set ___oserror
jmp ___mappederrno
-
- .bss
-
-TM: .tag tm
diff --git a/libsrc/apple2/gmtime_dt.s b/libsrc/apple2/gmtime_dt.s
new file mode 100644
index 000000000..a0b8e9f4d
--- /dev/null
+++ b/libsrc/apple2/gmtime_dt.s
@@ -0,0 +1,73 @@
+;
+; Oliver Schmidt, 14.08.2018
+; Colin Leroy-Mira, 2023
+;
+; struct tm * __fastcall__ gmtime_dt(const struct datetime *dt)
+;
+
+ .export _gmtime_dt, tm_buf
+
+ .include "time.inc"
+ .include "zeropage.inc"
+ .include "errno.inc"
+ .include "mli.inc"
+
+ ; Convert ProDOS date/time to a struct tm
+ ; source date address in AX
+ ; on stack:
+ ; destination struct
+
+_gmtime_dt:
+ sta ptr1
+ stx ptr1+1
+
+ ; Get time
+ ldy #$03
+ lda (ptr1),y
+ sta tm_buf + tm::tm_hour
+ dey
+ lda (ptr1),y
+ sta tm_buf + tm::tm_min
+
+ ; Get date
+ dey
+ lda (ptr1),y
+ lsr
+ php ; Save month msb
+ cmp #70 ; Year < 70?
+ bcs :+ ; No, leave alone
+ adc #100 ; Move 19xx to 20xx
+: sta tm_buf + tm::tm_year
+
+ dey
+ lda (ptr1),y
+ tax ; Save day
+ plp ; Restore month msb
+ ror
+ lsr
+ lsr
+ lsr
+ lsr
+ beq erange ; [1..12] allows for validity check
+ tay
+ dey ; Move [1..12] to [0..11]
+ sty tm_buf + tm::tm_mon
+ txa ; Restore day
+ and #%00011111
+ sta tm_buf + tm::tm_mday
+
+ lda #tm_buf
+ rts
+
+ ; Load errno code and return NULL
+erange: lda #ERANGE
+ sta ___errno
+ lda #$00
+ tax
+ rts
+
+ .bss
+
+tm_buf:
+ .tag tm
diff --git a/libsrc/apple2/gotoxy.s b/libsrc/apple2/gotoxy.s
index 6755af8d8..2ecd2a513 100644
--- a/libsrc/apple2/gotoxy.s
+++ b/libsrc/apple2/gotoxy.s
@@ -8,6 +8,10 @@
.export gotoxy, _gotoxy, _gotox
.import popa, VTABZ
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
+
.include "apple2.inc"
gotoxy:
@@ -22,4 +26,13 @@ _gotoxy:
_gotox:
sta CH ; Store X
- rts
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl :+
+ .endif
+
+ bit RD80VID ; In 80 column mode?
+ bpl :+
+ sta OURCH ; Store X
+: rts
diff --git a/libsrc/apple2/joy/a2.stdjoy.s b/libsrc/apple2/joy/a2.stdjoy.s
index 11be52eb4..b5e7a311b 100644
--- a/libsrc/apple2/joy/a2.stdjoy.s
+++ b/libsrc/apple2/joy/a2.stdjoy.s
@@ -92,7 +92,7 @@ COUNT:
bvc noiic ; Not $4x
dex ; Only one joystick for the //c
noiic: txa ; Number of joysticks we support
- ldx #$00
+ ldx #>$0000
rts
; READ routine. Read a particular joystick passed in A.
@@ -170,5 +170,5 @@ nogs2: lda #$00 ; 0 0 0 0 0 0 0 0
; Finalize
eor #%00010100 ; BTN_2 BTN_1 DOWN UP RIGHT LEFT 0 0
- ldx #$00
+ ldx #>$0000
rts
diff --git a/libsrc/apple2/lc-copy-applesoft.s b/libsrc/apple2/lc-copy-applesoft.s
new file mode 100644
index 000000000..70a04431d
--- /dev/null
+++ b/libsrc/apple2/lc-copy-applesoft.s
@@ -0,0 +1,9 @@
+;
+; Oliver Schmidt, 15.09.2009
+;
+; Copy the LC segment from the end of the binary to the Language Card
+; using AppleSoft's BLTU2 routine.
+;
+ .export bltu2
+
+bltu2 := $D39A
diff --git a/libsrc/apple2/libref.s b/libsrc/apple2/libref.s
index 8aa54abab..9c6994a5d 100644
--- a/libsrc/apple2/libref.s
+++ b/libsrc/apple2/libref.s
@@ -2,10 +2,8 @@
; Oliver Schmidt, 2013-05-31
;
- .export em_libref, mouse_libref, ser_libref, tgi_libref
+ .export em_libref, ser_libref
.import _exit
em_libref := _exit
-mouse_libref := _exit
ser_libref := _exit
-tgi_libref := _exit
diff --git a/libsrc/apple2/machinetype.s b/libsrc/apple2/machinetype.s
new file mode 100644
index 000000000..7fa70f29f
--- /dev/null
+++ b/libsrc/apple2/machinetype.s
@@ -0,0 +1,24 @@
+.ifndef __APPLE2ENH__
+
+ .constructor initmachinetype, 8
+
+ .import ostype
+ .export machinetype
+
+ .segment "ONCE"
+
+initmachinetype:
+ ldx ostype
+ cpx #$31 ; Apple //e enhanced?
+ ror machinetype ; Carry to high bit
+ cpx #$30 ; Apple //e?
+ ror machinetype
+ rts
+
+ .data
+
+; bit 7: Machine is a //e or newer
+; bit 6: Machine is a //e enhanced or newer
+machinetype: .byte 0
+
+.endif
diff --git a/libsrc/apple2/mcbdefault.s b/libsrc/apple2/mcbdefault.s
index 556a9d8fb..6a369114c 100644
--- a/libsrc/apple2/mcbdefault.s
+++ b/libsrc/apple2/mcbdefault.s
@@ -9,6 +9,10 @@
.export _mouse_def_callbacks
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
+
.include "apple2.inc"
; ------------------------------------------------------------------------
@@ -42,11 +46,14 @@ cursor = '+' | $40 ; Flashing crosshair
.endif
getcursor:
- .ifdef __APPLE2ENH__
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl column
+ .endif
bit RD80VID ; In 80 column mode?
bpl column ; No, skip bank switching
switch: bit LOWSCR ; Patched at runtime
- .endif
+
column: ldx #$00 ; Patched at runtime
getscr: lda $0400,x ; Patched at runtime
cmp #cursor
@@ -55,9 +62,7 @@ getscr: lda $0400,x ; Patched at runtime
setcursor:
lda #cursor
setscr: sta $0400,x ; Patched at runtime
- .ifdef __APPLE2ENH__
bit LOWSCR ; Doesn't hurt in 40 column mode
- .endif
rts
; ------------------------------------------------------------------------
@@ -65,9 +70,7 @@ setscr: sta $0400,x ; Patched at runtime
.code
done:
- .ifdef __APPLE2ENH__
bit LOWSCR ; Doesn't hurt in 40 column mode
- .endif
return: rts
; Hide the mouse cursor.
@@ -108,14 +111,14 @@ movex:
inx
bcs :-
stx column+1
- .ifdef __APPLE2ENH__
+
+ ; Patch switch anyway, it will just be skipped over if in 40-col mode
adc #7 / 2 ; Left or right half of 40-col column?
ldx #
+;
+; time_t __fastcall__ mktime_dt(const struct datetime *dt)
+;
+
+ .import steaxspidx, pushax, incsp2, _gmtime_dt
+ .import tm_buf
+ .export _mktime_dt
+
+ .include "time.inc"
+ .include "zeropage.inc"
+ .include "errno.inc"
+ .include "mli.inc"
+
+ ; Convert ProDOS date/time to UNIX timestamp
+ ; source date address in AX
+
+_mktime_dt:
+ ; Convert to internal tm
+ jsr _gmtime_dt
+ cpx #$00
+ bne :+
+ cmp #$00
+ beq err
+
+ ; Make time_t
+: lda #tm_buf
+ jmp _mktime
+
+err: lda #$00
+ tax
+ sta sreg
+ sta sreg+1
+ rts
diff --git a/libsrc/apple2/mli.inc b/libsrc/apple2/mli.inc
index 42363d9c9..382a071b0 100644
--- a/libsrc/apple2/mli.inc
+++ b/libsrc/apple2/mli.inc
@@ -83,8 +83,8 @@ EOF_COUNT = 2
AUX_TYPE .word
STORAGE_TYPE .byte
BLOCKS .word
- MODE_DATE .word
- MODE_TIME .word
+ MOD_DATE .word
+ MOD_TIME .word
CREATE_DATE .word
CREATE_TIME .word
.endstruct
@@ -139,3 +139,6 @@ LEVEL := $BF94 ; File level: used in open, flush, close
MACHID := $BF98 ; Machine identification
PFIXPTR := $BF9A ; If = 0, no prefix active
KVERSION:= $BFFF ; Kernel version number
+
+; Max filename length
+FILENAME_MAX = 64+1
diff --git a/libsrc/apple2/mli_file_info.s b/libsrc/apple2/mli_file_info.s
new file mode 100644
index 000000000..16e01c07f
--- /dev/null
+++ b/libsrc/apple2/mli_file_info.s
@@ -0,0 +1,33 @@
+;
+; Colin Leroy-Mira, 2023
+;
+
+ .export mli_file_info
+ .import pushname, popname, mli_file_info_direct
+ .import popax
+ .include "zeropage.inc"
+ .include "errno.inc"
+ .include "mli.inc"
+
+ ; Calls ProDOS MLI GET_FILE_INFO on the filename
+ ; stored as C string in AX at top of stack
+ ; Returns with carry set on error, and sets errno
+mli_file_info:
+ ; Get pathname
+ jsr popax
+ jsr pushname
+ bne oserr
+
+ jsr mli_file_info_direct
+ php ; Save return status
+
+ jsr popname ; Preserves A
+
+ plp
+ bcs oserr
+ rts
+
+oserr:
+ jsr ___mappederrno
+ sec
+ rts
diff --git a/libsrc/apple2/mli_file_info_direct.s b/libsrc/apple2/mli_file_info_direct.s
new file mode 100644
index 000000000..c15ebc28f
--- /dev/null
+++ b/libsrc/apple2/mli_file_info_direct.s
@@ -0,0 +1,22 @@
+;
+; Colin Leroy-Mira, 2023
+;
+
+ .export mli_file_info_direct
+ .include "zeropage.inc"
+ .include "mli.inc"
+
+ ; Calls ProDOS MLI GET_FILE_INFO on the ProDOS style
+ ; filename stored on top of stack
+ ; Returns with carry set on error, and sets errno
+mli_file_info_direct:
+ ; Set pushed name
+ lda sp
+ ldx sp+1
+ sta mliparam + MLI::INFO::PATHNAME
+ stx mliparam + MLI::INFO::PATHNAME+1
+
+ ; Get file information
+ lda #GET_INFO_CALL
+ ldx #GET_INFO_COUNT
+ jmp callmli
diff --git a/libsrc/apple2/mou/a2.stdmou.s b/libsrc/apple2/mou/a2.stdmou.s
index c54c09d34..9c2f96200 100644
--- a/libsrc/apple2/mou/a2.stdmou.s
+++ b/libsrc/apple2/mou/a2.stdmou.s
@@ -7,6 +7,7 @@
.include "zeropage.inc"
.include "mouse-kernel.inc"
.include "apple2.inc"
+ .include "get_tv.inc"
.macpack module
@@ -21,6 +22,7 @@ CLAMPMOUSE = $17 ; Sets mouse bounds in a window
HOMEMOUSE = $18 ; Sets mouse to upper-left corner of clamp win
INITMOUSE = $19 ; Resets mouse clamps to default values and
; sets mouse position to 0,0
+TIMEDATA = $1C ; Set mousecard's interrupt rate
pos1_lo := $0478
pos1_hi := $0578
@@ -41,6 +43,7 @@ status := $0778
.byte MOUSE_API_VERSION ; Mouse driver API version number
; Library reference
+libref:
.addr $0000
; Jump table
@@ -155,6 +158,7 @@ next: inc ptr1+1
; Disable interrupts now because setting the slot number makes
; the IRQ handler (maybe called due to some non-mouse IRQ) try
; calling the firmware which isn't correctly set up yet
+ php
sei
; Convert to and save slot number
@@ -168,7 +172,31 @@ next: inc ptr1+1
asl
sta yparam+1
- ; The AppleMouse II Card needs the ROM switched in
+ ; Apple II technical notes "Varying VBL Interrupt Rate",
+ lda libref
+ ldx libref+1
+ sta ptr1
+ stx ptr1+1
+
+ .ifdef __APPLE2ENH__
+ lda (ptr1)
+ .else
+ ldy #$00
+ lda (ptr1),y
+ .endif
+
+ cmp #TV::OTHER
+ beq :+
+
+ ; The TV values are aligned with the values the mousecard
+ ; expect: 0 for 60Hz, 1 for 50Hz.
+ .assert TV::NTSC = 0, error
+ .assert TV::PAL = 1, error
+
+ ldx #TIMEDATA
+ jsr firmware
+
+: ; The AppleMouse II Card needs the ROM switched in
; to be able to detect an Apple //e and use RDVBL
bit $C082
@@ -211,7 +239,7 @@ next: inc ptr1+1
common: jsr firmware
; Enable interrupts and return success
- cli
+ plp
lda #MOUSE_ERR_OK
rts
@@ -220,6 +248,7 @@ common: jsr firmware
; No return code required (the driver is removed from memory on return).
UNINSTALL:
; Hide cursor
+ php
sei
jsr CHIDE
@@ -249,7 +278,8 @@ SETBOX:
; Apple II Mouse TechNote #1, Interrupt Environment with the Mouse:
; "Disable interrupts before placing position information in the
; screen holes."
-: sei
+: php
+ sei
; Set low clamp
lda (ptr1),y
@@ -298,6 +328,7 @@ GETBOX:
; the screen). No return code required.
MOVE:
ldy slot
+ php
sei
; Set y
@@ -328,9 +359,10 @@ MOVE:
; no special action is required besides hiding the mouse cursor.
; No return code required.
HIDE:
+ php
sei
jsr CHIDE
- cli
+ plp
rts
; SHOW: Is called to show the mouse cursor. The mouse kernel manages a
@@ -339,15 +371,16 @@ HIDE:
; no special action is required besides enabling the mouse cursor.
; No return code required.
SHOW:
+ php
sei
jsr CSHOW
- cli
+ plp
rts
; BUTTONS: Return the button mask in A/X.
BUTTONS:
lda info + MOUSE_INFO::BUTTONS
- ldx #$00
+ ldx #>$0000
rts
; POS: Return the mouse position in the MOUSE_POS struct pointed to by ptr1.
@@ -360,12 +393,13 @@ POS:
; struct pointed to by ptr1. No return code required.
INFO:
ldy #.sizeof(MOUSE_INFO)-1
-copy: sei
+copy: php
+ sei
: lda info,y
sta (ptr1),y
dey
bpl :-
- cli
+ plp
rts
; IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
diff --git a/libsrc/apple2/mouseref.s b/libsrc/apple2/mouseref.s
new file mode 100644
index 000000000..b0e75c367
--- /dev/null
+++ b/libsrc/apple2/mouseref.s
@@ -0,0 +1,25 @@
+;
+; Colin Leroy-Mira, 2025-05-11
+;
+
+ .export mouse_libref
+ .import _get_tv, ostype, return0
+
+ .constructor init_mousetv
+
+ .include "get_tv.inc"
+
+ .segment "ONCE"
+
+.proc init_mousetv
+ lda ostype
+ cmp #$40 ; Technical notes say not to change
+ bcs :+ ; interrupt rate on IIc/IIgs, so...
+ jsr _get_tv
+ sta mouse_libref
+: rts ; ...don't update "Other" on those machines
+.endproc
+
+ .data
+
+mouse_libref: .byte TV::OTHER
diff --git a/libsrc/apple2/chline.s b/libsrc/apple2/mtchline.s
similarity index 68%
rename from libsrc/apple2/chline.s
rename to libsrc/apple2/mtchline.s
index be157ca9e..71347c563 100644
--- a/libsrc/apple2/chline.s
+++ b/libsrc/apple2/mtchline.s
@@ -1,27 +1,26 @@
;
; Ullrich von Bassewitz, 08.08.1998
+; Colin Leroy-Mira, 26.05.2025
;
-; void __fastcall__ chlinexy (unsigned char x, unsigned char y, unsigned char length);
-; void __fastcall__ chline (unsigned char length);
+; void __fastcall__ mt_chlinexy (unsigned char x, unsigned char y, unsigned char length);
+; void __fastcall__ mt_chline (unsigned char length);
;
- .export _chlinexy, _chline, chlinedirect
+.ifdef __APPLE2ENH__
+
+ .export _mt_chlinexy, _mt_chline, chlinedirect
.import gotoxy, cputdirect
.include "zeropage.inc"
.include "apple2.inc"
-_chlinexy:
+_mt_chlinexy:
pha ; Save the length
jsr gotoxy ; Call this one, will pop params
pla ; Restore the length and run into _chline
-_chline:
- .ifdef __APPLE2ENH__
+_mt_chline:
ldx #'_' | $80 ; Underscore, screen code
- .else
- ldx #'-' | $80 ; Minus, screen code
- .endif
chlinedirect:
stx tmp1
@@ -33,3 +32,5 @@ chlinedirect:
dec tmp2
bne :-
done: rts
+
+.endif
diff --git a/libsrc/apple2/cvline.s b/libsrc/apple2/mtcvline.s
similarity index 57%
rename from libsrc/apple2/cvline.s
rename to libsrc/apple2/mtcvline.s
index 86bbf11f4..03e11bf51 100644
--- a/libsrc/apple2/cvline.s
+++ b/libsrc/apple2/mtcvline.s
@@ -1,34 +1,32 @@
;
; Ullrich von Bassewitz, 08.08.1998
+; Colin Leroy-Mira, 26.05.2025
;
-; void __fastcall__ cvlinexy (unsigned char x, unsigned char y, unsigned char length);
-; void __fastcall__ cvline (unsigned char length);
+; void __fastcall__ mt_cvlinexy (unsigned char x, unsigned char y, unsigned char length);
+; void __fastcall__ mt_cvline (unsigned char length);
;
- .export _cvlinexy, _cvline
+.ifdef __APPLE2ENH__
+
+ .export _mt_cvlinexy, _mt_cvline
.import gotoxy, putchar, newline
.include "zeropage.inc"
-_cvlinexy:
+_mt_cvlinexy:
pha ; Save the length
jsr gotoxy ; Call this one, will pop params
pla ; Restore the length and run into _cvline
-_cvline:
- .ifdef __APPLE2ENH__
- ldx #$5F ; Left vertical line MouseText character
- .else
- ldx #'!' | $80 ; Exclamation mark, screen code
- .endif
-
- stx tmp1
+_mt_cvline:
cmp #$00 ; Is the length zero?
beq done ; Jump if done
sta tmp2
-: lda tmp1 ; Screen code
+: lda #$5F ; Left vertical line MouseText character
jsr putchar ; Write, no cursor advance
jsr newline ; Advance cursor to next line
dec tmp2
bne :-
done: rts
+
+.endif
diff --git a/libsrc/apple2/open.s b/libsrc/apple2/open.s
index 68c203cd6..7ece7f18d 100644
--- a/libsrc/apple2/open.s
+++ b/libsrc/apple2/open.s
@@ -18,6 +18,7 @@
.include "fcntl.inc"
.include "mli.inc"
.include "filedes.inc"
+ .include "time.inc"
.segment "ONCE"
@@ -208,7 +209,7 @@ done: lda tmp1 ; Restore fd
jsr popname ; Preserves A
; Return success
- ldx #$00
+ ldx #>$0000
stx ___oserror
rts
diff --git a/libsrc/apple2/opendir.c b/libsrc/apple2/opendir.c
deleted file mode 100644
index 1144d8511..000000000
--- a/libsrc/apple2/opendir.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*****************************************************************************/
-/* */
-/* opendir.h */
-/* */
-/* Open a directory */
-/* */
-/* */
-/* */
-/* (C) 2005 Oliver Schmidt, */
-/* */
-/* */
-/* 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
-#include
-#include
-#include
-#include
-#include
-#include
-#include "dir.h"
-
-
-
-/*****************************************************************************/
-/* Data */
-/*****************************************************************************/
-
-
-
-extern char _cwd[FILENAME_MAX];
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-DIR* __fastcall__ opendir (register const char* name)
-{
- register DIR* dir;
-
- /* Alloc DIR */
- if ((dir = malloc (sizeof (*dir))) == NULL) {
-
- /* May not have been done by malloc() */
- _directerrno (ENOMEM);
-
- /* Return failure */
- return NULL;
- }
-
- /* Interpret dot as current working directory */
- if (*name == '.') {
- name = _cwd;
- }
-
- /* Open directory file */
- if ((dir->fd = open (name, O_RDONLY)) != -1) {
-
- /* Read directory key block */
- if (read (dir->fd,
- dir->block.bytes,
- sizeof (dir->block)) == sizeof (dir->block)) {
-
- /* Get directory entry infos from directory header */
- dir->entry_length = dir->block.bytes[0x23];
- dir->entries_per_block = dir->block.bytes[0x24];
-
- /* Skip directory header entry */
- dir->current_entry = 1;
-
- /* Return success */
- return dir;
- }
-
- /* EOF: Most probably no directory file at all */
- if (_oserror == 0) {
- _directerrno (EINVAL);
- }
-
- /* Cleanup directory file */
- close (dir->fd);
- }
-
- /* Cleanup DIR */
- free (dir);
-
- /* Return failure */
- return NULL;
-}
diff --git a/libsrc/apple2/opendir.s b/libsrc/apple2/opendir.s
new file mode 100644
index 000000000..317be2755
--- /dev/null
+++ b/libsrc/apple2/opendir.s
@@ -0,0 +1,159 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; DIR* __fastcall__ opendir (register const char* name)
+;
+
+ .export _opendir, read_dir_block_ptr1
+
+ .import closedir_ptr1
+ .import _open, _read, _close
+ .import _malloc
+ .import ___directerrno
+
+ .import ___oserror, __cwd
+
+ .import pushptr1, popptr1
+ .import pushax, pusha0
+ .import return0, returnFFFF
+
+ .importzp ptr1
+
+ .include "apple2.inc"
+ .include "dir.inc"
+ .include "errno.inc"
+ .include "fcntl.inc"
+ .include "zeropage.inc"
+
+.proc _opendir
+ sta ptr1
+ stx ptr1+1
+
+ ldy #$00
+ lda (ptr1),y
+ cmp #'.'
+ bne :+
+
+ lda #<__cwd
+ ldx #>__cwd
+ sta ptr1
+ stx ptr1+1
+
+: ; Open directory
+ jsr pushptr1
+ lda #O_RDONLY
+ jsr pusha0
+
+ ldy #$04
+ jsr _open
+
+ cmp #$FF ; Did we succeed?
+ beq @return_null
+ pha ; Yes - Push fd for backup
+
+ ; malloc the dir struct
+ lda #<.sizeof(DIR)
+ ldx #>.sizeof(DIR)
+ jsr _malloc
+ bne :+
+
+ ; We failed to allocate
+ pla ; Get fd back
+ ldx #$00
+ jsr _close ; Close it
+
+ lda #ENOMEM ; Set error
+ jsr ___directerrno
+@return_null:
+ jmp return0
+
+: ; Store dir struct to pointer
+ sta ptr1
+ stx ptr1+1
+
+ ; Save fd to dir struct
+ lda #$00
+ ldy #DIR::FD + 1
+ sta (ptr1),y
+
+ dey
+ pla ; Get fd back
+ sta (ptr1),y
+
+ jsr read_dir_block_ptr1
+ bcc @read_ok
+
+ ; Close directory, free it
+ jsr closedir_ptr1
+ jmp return0 ; Return NULL
+
+@read_ok:
+ ; Read succeeded, populate dir struct
+
+ ; Get file_count to entry_length from block
+ ldy #$26 + DIR::BYTES
+: lda (ptr1),y
+ pha
+ dey
+ cpy #$23 + DIR::BYTES - 1
+ bne :-
+
+ ; Set entry_length to file_count in struct
+ ldy #DIR::ENTRY_LENGTH
+: pla
+ sta (ptr1),y
+ iny
+ cpy #DIR::CURRENT_ENTRY
+ bne :-
+
+ ; Skip directory header entry
+ lda #$01
+ sta (ptr1),y
+
+ ; Return pointer to dir struct
+ lda ptr1
+ ldx ptr1+1
+ rts
+.endproc
+
+; Read a directory for the DIR* pointer in ptr1
+; Return with carry clear on success
+read_dir_block_ptr1:
+ ; Push ptr1, read will destroy it
+ jsr pushptr1
+
+ ldy #DIR::FD
+ lda (ptr1),y
+
+ jsr pusha0 ; Push fd for read
+ lda #DIR::BYTES
+ adc ptr1+1
+ tax
+ pla
+ jsr pushax ; Push dir->block.bytes for read
+
+ lda #<.sizeof(DIR::BYTES)
+ ldx #>.sizeof(DIR::BYTES)
+
+ jsr _read ; Read directory block
+ cpx #>.sizeof(DIR::BYTES)
+ bne @read_err
+ cmp #<.sizeof(DIR::BYTES)
+ beq @read_ok
+
+@read_err:
+ ; Read failed, exit
+ lda ___oserror
+ bne :+
+ lda #EINVAL
+ jsr ___directerrno
+: sec
+ bcs @out
+@read_ok:
+ clc
+@out:
+ jmp popptr1
diff --git a/libsrc/apple2/oserror.s b/libsrc/apple2/oserror.s
index 5f523340f..db4f146cd 100644
--- a/libsrc/apple2/oserror.s
+++ b/libsrc/apple2/oserror.s
@@ -23,7 +23,7 @@ ___osmaperrno:
; Found the code
: lda ErrTab-1,x
- ldx #$00 ; High byte always zero
+ ldx #>$0000
rts
.rodata
diff --git a/libsrc/apple2/readdir.c b/libsrc/apple2/readdir.c
deleted file mode 100644
index 8acfbbe8b..000000000
--- a/libsrc/apple2/readdir.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*****************************************************************************/
-/* */
-/* readdir.c */
-/* */
-/* Read directory entry */
-/* */
-/* */
-/* */
-/* (C) 2005 Oliver Schmidt, */
-/* */
-/* */
-/* 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
-#include
-#include
-#include "dir.h"
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-struct dirent* __fastcall__ readdir (register DIR* dir)
-{
- register unsigned char* entry;
-
- /* Search for the next active directory entry */
- do {
-
- /* Read next directory block if necessary */
- if (dir->current_entry == dir->entries_per_block) {
- if (read (dir->fd,
- dir->block.bytes,
- sizeof (dir->block)) != sizeof (dir->block)) {
-
- /* Just return failure as read() has */
- /* set errno if (and only if) no EOF */
- return NULL;
- }
-
- /* Start with first entry in next block */
- dir->current_entry = 0;
- }
-
- /* Compute pointer to current entry */
- entry = dir->block.content.entries +
- dir->current_entry * dir->entry_length;
-
- /* Switch to next entry */
- ++dir->current_entry;
- } while (entry[0x00] == 0);
-
- /* Move creation date/time to allow for next step below */
- *(unsigned long*)&entry[0x1A] = *(unsigned long*)&entry[0x18];
-
- /* Feature unsigned long access to EOF by extension from 3 to 4 bytes */
- entry[0x18] = 0;
-
- /* Move file type to allow for next step below */
- entry[0x19] = entry[0x10];
-
- /* Zero-terminate file name */
- entry[0x01 + (entry[0x00] & 0x0F)] = 0;
-
- /* Return success */
- return (struct dirent*)&entry[0x01];
-}
diff --git a/libsrc/apple2/readdir.s b/libsrc/apple2/readdir.s
new file mode 100644
index 000000000..e748055e1
--- /dev/null
+++ b/libsrc/apple2/readdir.s
@@ -0,0 +1,113 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; struct dirent * __fastcall__ readdir (DIR *dir)
+;
+ .export _readdir
+ .import read_dir_block_ptr1
+
+ .import incax1, return0
+ .import tosaddax, tosumula0, incaxy
+ .import pushax, pusha0, pushptr1, popptr1
+ .importzp ptr1, ptr4
+
+ .include "dir.inc"
+
+.proc _readdir
+ sta ptr1
+ stx ptr1+1
+
+@next_entry:
+ ; Do we need to read the next directory block?
+ ldy #DIR::CURRENT_ENTRY
+ lda (ptr1),y
+ ldy #DIR::ENTRIES_PER_BLOCK
+ cmp (ptr1),y
+ bne @read_entry ; We don't
+
+ jsr read_dir_block_ptr1
+ bcc @read_ok
+
+ ; We had a read error
+ jmp return0
+
+@read_ok:
+ ldy #DIR::CURRENT_ENTRY
+ lda #$00
+ sta (ptr1),y
+
+@read_entry:
+ ; Compute pointer to current entry:
+ ; entry = dir->block.content.entries +
+ ; dir->current_entry * dir->entry_length
+
+ jsr pushptr1 ; Backup ptr1
+ lda ptr1
+ ldx ptr1+1
+ ldy #DIR::BYTES + DIR::CONTENT::ENTRIES
+ jsr incaxy
+ jsr pushax
+ ldy #DIR::CURRENT_ENTRY
+ lda (ptr1),y
+ jsr pusha0
+ ldy #DIR::ENTRY_LENGTH
+ lda (ptr1),y
+ jsr tosumula0
+ jsr tosaddax
+ ; Store pointer to current entry
+ sta ptr4
+ stx ptr4+1
+ jsr popptr1
+
+ ; Switch to next entry
+ ldy #DIR::CURRENT_ENTRY
+ lda (ptr1),y
+ clc
+ adc #1
+ sta (ptr1),y
+
+ ; Check if entry[0] == 0
+ ldy #$00
+ lda (ptr4),y
+ beq @next_entry ; Yes, skip entry
+
+ ; Move creation date/time to allow for next step below
+ ; 18-19-1A-1B => 1A-1B-1C-1D
+ ldy #$1B
+: lda (ptr4),y
+ iny
+ iny
+ sta (ptr4),y
+ dey
+ dey
+ dey
+ cpy #$17
+ bne :-
+
+ ; Feature unsigned long access to EOF by extension from 3 to 4 bytes
+ ; entry[0x18] = 0
+ iny
+ lda #$00
+ sta (ptr4),y
+
+ ; Move file type to allow for next step below
+ ; entry[0x19] = entry[0x10]
+ ldy #$10
+ lda (ptr4),y
+ ldy #$19
+ sta (ptr4),y
+
+ ; Zero-terminate file name
+ ldy #$00
+ lda (ptr4),y
+ and #$0F
+ tay
+ iny
+ lda #$00
+ sta (ptr4),y
+
+ ; Return pointer to entry+1
+ lda ptr4
+ ldx ptr4+1
+ jmp incax1
+.endproc
diff --git a/libsrc/apple2/revers.s b/libsrc/apple2/revers.s
index 83fcb2ae7..86dfd0d19 100644
--- a/libsrc/apple2/revers.s
+++ b/libsrc/apple2/revers.s
@@ -18,5 +18,5 @@ normal: dex ; $00->$FF, $40->$3F
stx INVFLG ; Save new flag value
bmi :+ ; Jump if current value is $FF (normal)
lda #$01 ; Return "inverse"
-: ldx #$00
+: ldx #>$0000
rts
diff --git a/libsrc/apple2/rewinddir.s b/libsrc/apple2/rewinddir.s
new file mode 100644
index 000000000..fd3e5738f
--- /dev/null
+++ b/libsrc/apple2/rewinddir.s
@@ -0,0 +1,70 @@
+;
+; Colin Leroy-Mira , 2024
+;
+; void __fastcall__ rewinddir (DIR* dir)
+;
+ .export _rewinddir
+ .import read_dir_block_ptr1
+
+ .import pusha, pusha0, pushax
+ .import pushptr1, popptr1
+ .import incaxy
+ .import _lseek, _memset
+
+ .importzp ptr1, sreg
+
+ .include "dir.inc"
+ .include "stdio.inc"
+
+.proc _rewinddir
+ sta ptr1
+ stx ptr1+1
+ jsr pushptr1 ; Backup ptr1, destroyed by _lseek
+
+ ; Rewind directory file
+ ldy #DIR::FD
+ lda (ptr1),y
+ jsr pusha0 ; Push dir->fd
+
+ tya ; Y = 0 here
+ jsr pusha0
+ jsr pusha0 ; Push 0L
+
+ lda #SEEK_SET ; X = 0 here
+ jsr _lseek
+
+ ora sreg ; Check lseek returned 0L
+ ora sreg+1
+ bne @rewind_err
+ txa
+ bne @rewind_err
+
+ jsr popptr1 ; Restore ptr1
+
+ ; Read directory key block
+ jsr read_dir_block_ptr1
+ bcs @rewind_err
+
+ ; Skip directory header entry
+ lda #$01
+ ldy #DIR::CURRENT_ENTRY
+ sta (ptr1),y
+ rts
+
+@rewind_err:
+ jsr popptr1 ; Restore ptr1
+
+ ; Assert that no subsequent readdir() finds an active entry
+ lda ptr1
+ ldx ptr1+1
+ ldy #DIR::BYTES + DIR::CONTENT::ENTRIES
+ jsr incaxy
+ jsr pushax
+
+ lda #$00
+ jsr pusha
+
+ lda #<.sizeof(DIR::BYTES)
+ ldx #>.sizeof(DIR::BYTES)
+ jmp _memset
+.endproc
diff --git a/libsrc/apple2/ser/a2.gs.s b/libsrc/apple2/ser/a2.gs.s
index 3a2db1926..3ad899fc2 100644
--- a/libsrc/apple2/ser/a2.gs.s
+++ b/libsrc/apple2/ser/a2.gs.s
@@ -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.
@@ -286,7 +295,7 @@ SER_CLOSE:
bcc IIgs
lda #SER_ERR_NO_DEVICE ; Not a IIgs
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
IIgs:
@@ -295,8 +304,9 @@ IIgs:
ldx Channel
- ; Deactivate interrupts
- sei
+ php ; Deactivate interrupts
+ sei ; if enabled
+
ldy #WR_MASTER_IRQ_RST
lda #MASTER_IRQ_SHUTDOWN
jsr writeSCCReg
@@ -325,16 +335,26 @@ IIgs:
ldx #$00
stx Opened ; Mark port as closed
- cli
+ plp ; Reenable interrupts if needed
: 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.
SER_OPEN:
- sei
+ php ; Deactivate interrupts
+ sei ; if enabled
; Check if the handshake setting is valid
ldy #SER_PARAMS::HANDSHAKE ; Handshake
@@ -360,11 +380,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 +399,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 +434,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,23 +492,16 @@ 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
lda #SER_ERR_OK
SetupOut:
- ldx #$00 ; Promote char return value
+ plp ; Reenable interrupts if needed
+ ldx #>$0000
sty Opened
- cli
rts
;----------------------------------------------------------------------------
@@ -529,7 +539,7 @@ SER_GET:
rts
NoData:
lda #SER_ERR_NO_DATA
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
;----------------------------------------------------------------------------
@@ -550,7 +560,7 @@ SER_PUT:
: ldy SendFreeCnt ; Do we have room to store byte?
bne :+
lda #SER_ERR_OVERFLOW
- ldx #$00
+ ldx #>$0000
rts
: ldy SendTail ; Put byte into send buffer & send
@@ -561,7 +571,7 @@ SER_PUT:
jsr TryToSend
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
- tax
+ tax ; Promote char return value
rts
;----------------------------------------------------------------------------
@@ -598,11 +608,11 @@ SER_IOCTL:
stx Channel
.assert SER_ERR_OK = 0, error
- tax
+ tax ; Promote char return value
rts
: lda #SER_ERR_INV_IOCTL
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
;----------------------------------------------------------------------------
diff --git a/libsrc/apple2/ser/a2.ssc.s b/libsrc/apple2/ser/a2.ssc.s
index c8aa6e9a5..488a32540 100644
--- a/libsrc/apple2/ser/a2.ssc.s
+++ b/libsrc/apple2/ser/a2.ssc.s
@@ -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,14 +333,19 @@ 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
lda #SER_ERR_OK
Out:
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
;----------------------------------------------------------------------------
@@ -354,7 +360,7 @@ SER_GET:
cmp #$FF
bne :+
lda #SER_ERR_NO_DATA
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
: ldy Stopped ; Check for flow stopped
@@ -402,7 +408,7 @@ SER_PUT:
ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend
bne :+
lda #SER_ERR_OVERFLOW
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
: ldy SendTail ; Put byte into send buffer
@@ -450,7 +456,7 @@ SER_IOCTL:
rts
: lda #SER_ERR_INV_IOCTL
- ldx #$00 ; Promote char return value
+ ldx #>$0000
rts
;----------------------------------------------------------------------------
diff --git a/libsrc/apple2/set_iigs_speed.s b/libsrc/apple2/set_iigs_speed.s
new file mode 100644
index 000000000..f13e8ab6a
--- /dev/null
+++ b/libsrc/apple2/set_iigs_speed.s
@@ -0,0 +1,29 @@
+;
+; Colin Leroy-Mira , 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 #>$0000
+ rts
diff --git a/libsrc/apple2/sleep.s b/libsrc/apple2/sleep.s
new file mode 100644
index 000000000..43873d9f4
--- /dev/null
+++ b/libsrc/apple2/sleep.s
@@ -0,0 +1,54 @@
+;
+; Colin Leroy-Mira , 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
diff --git a/libsrc/apple2/stat.s b/libsrc/apple2/stat.s
new file mode 100644
index 000000000..e0564ae0c
--- /dev/null
+++ b/libsrc/apple2/stat.s
@@ -0,0 +1,129 @@
+;
+; Colin Leroy-Mira, 2023
+;
+; int __fastcall__ stat(const char *pathname, struct stat *statbuf);
+;
+
+ .export _stat
+ .import __errno, _open,_close
+ .import mli_file_info
+ .import popax, pushax, pusha0, incsp2
+ .include "zeropage.inc"
+ .include "errno.inc"
+ .include "fcntl.inc"
+ .include "filedes.inc"
+ .include "mli.inc"
+ .include "stat.inc"
+
+_stat:
+ ; Store statbuf pointer
+ sta ptr4
+ sta stbuf
+ stx ptr4+1
+ stx stbuf+1
+
+ ; Clear statbuf
+ lda #$00
+ ldy #.sizeof(stat)-1
+: sta (ptr4),y
+ dey
+ bpl :-
+
+ ; Reset errno
+ sta ___errno
+
+ ; Store pathname
+ jsr popax
+ jsr pushax ; Push it back for mli_file_info
+ jsr pushax ; and for open
+
+ jsr mli_file_info
+
+ bcc got_info
+ jmp incsp2 ; Drop filename copy for open
+
+got_info:
+ ; st_dev
+ lda DEVNUM
+ lsr ; Shift right to cc65 representation
+ lsr
+ lsr
+ lsr
+ ldy #stat::st_dev
+ sta (ptr4),y
+
+ ; st_mode (S_IFDIR/S_IFREG only)
+ lda mliparam + MLI::INFO::FILE_TYPE
+ ldy #stat::st_mode
+ cmp #$0f
+ bne is_reg
+ lda #S_IFDIR
+ bne set_st_mode
+
+is_reg: lda #S_IFREG
+
+set_st_mode:
+ sta (ptr4),y
+
+ ; st_access through st_create_time
+ ldx #MLI::INFO::ACCESS
+ ldy #stat::st_access
+: lda mliparam,x
+ sta (ptr4),y
+ inx
+ iny
+ cpy #stat::st_create_time + .sizeof(stat::st_create_time)
+ bne :-
+
+ ; st_size
+ lda #O_RDONLY
+ jsr pusha0
+ ldy #$04
+ jsr _open
+ cmp #$FF
+ beq done
+ pha ; Save file descriptor for closing
+
+ ; Get ProDOS's REF_NUM from file descriptor
+ jsr getfd
+ ; Get file information
+ sta mliparam + MLI::EOF::REF_NUM
+ lda #GET_EOF_CALL
+ ldx #EOF_COUNT
+ jsr callmli
+ bcs eoferr
+
+ ; Get struct stat in ptr4 back, open destroyed it
+ lda stbuf
+ ldx stbuf+1
+ sta ptr4
+ stx ptr4+1
+
+ ; Store size
+ ldy #stat::st_size
+ lda mliparam + MLI::EOF::EOF
+ sta (ptr4),y
+ lda mliparam + MLI::EOF::EOF+1
+ iny
+ sta (ptr4),y
+ lda mliparam + MLI::EOF::EOF+2
+ iny
+ sta (ptr4),y
+
+ ; Close file
+eoferr:
+ pla
+ ldx #$00
+ jsr _close
+
+ ; Set return value if we had an error
+ lda ___errno
+ beq done
+ lda #$FF
+done:
+ tax ; Promote char return value
+ rts
+
+ .bss
+
+stbuf: .res 2
diff --git a/libsrc/apple2/statvfs.s b/libsrc/apple2/statvfs.s
new file mode 100644
index 000000000..8fcf46af8
--- /dev/null
+++ b/libsrc/apple2/statvfs.s
@@ -0,0 +1,123 @@
+;
+; Colin Leroy-Mira, 2023
+;
+; int __fastcall__ statvfs(const char *pathname, struct statvfs *statvfsbuf);
+;
+
+ .export _statvfs
+ .import _dio_query_sectsize
+ .import mli_file_info, pushax, popax, popptr1, pushptr1
+ .include "zeropage.inc"
+ .include "apple2.inc"
+ .include "errno.inc"
+ .include "mli.inc"
+ .include "statvfs.inc"
+
+_statvfs:
+ ; Store statbuf
+ sta ptr4
+ stx ptr4+1
+
+ ; Clear statbuf
+ lda #$00
+ ldy #.sizeof(statvfs)-1
+: sta (ptr4),y
+ dey
+ bpl :-
+
+ ; Store pathname, keeping only volume name
+ jsr popptr1
+ ldy #$00
+ sty vol_sep
+ lda (ptr1),y
+ cmp #'/' ; Is the path absolute?
+ beq :+
+ lda #EINVAL
+ jmp ___directerrno
+
+: iny
+ lda (ptr1),y
+ beq :+ ; End of string, no other /
+ cpy #FILENAME_MAX
+ beq :+ ; Max filename length reached
+ cmp #'/'
+ bne :- ; Not a slash, keep looking
+ sty vol_sep ; Register '/' index
+ lda #$00
+ sta (ptr1),y ; Cut pathname at first slash
+: jsr pushptr1
+
+ jsr mli_file_info
+
+ php
+ ldy vol_sep ; Put slash back in pathname
+ lda #'/'
+ sta (ptr1),y
+ plp
+
+ bcc got_info
+
+ jmp ___mappederrno
+
+got_info:
+ ; f_fsid
+ lda DEVNUM
+ lsr ; Shift right to cc65 representation
+ lsr
+ lsr
+ lsr
+ ldy #statvfs::f_fsid
+ sta (ptr4),y
+
+ ; total number of blocks
+ lda mliparam + MLI::INFO::AUX_TYPE
+ ldy #statvfs::f_blocks
+ sta (ptr4),y
+ lda mliparam + MLI::INFO::AUX_TYPE+1
+ iny
+ sta (ptr4),y
+
+ ; blocks free & avail
+ sec
+ lda mliparam + MLI::INFO::AUX_TYPE
+ sbc mliparam + MLI::INFO::BLOCKS
+ ldy #statvfs::f_bfree
+ sta (ptr4),y
+ ldy #statvfs::f_bavail
+ sta (ptr4),y
+
+ lda mliparam + MLI::INFO::AUX_TYPE+1
+ sbc mliparam + MLI::INFO::BLOCKS+1
+ iny
+ sta (ptr4),y
+ ldy #statvfs::f_bfree+1
+ sta (ptr4),y
+
+ ; block sizes
+ jsr _dio_query_sectsize
+ ; low bytes
+ ldy #statvfs::f_bsize
+ sta (ptr4),y
+ ldy #statvfs::f_frsize
+ sta (ptr4),y
+ ; f_frsize high byte
+ iny
+ txa
+ sta (ptr4),y
+ ; f_bsize high byte
+ ldy #statvfs::f_bsize+1
+ sta (ptr4),y
+
+ ; f_namemax
+ lda #FILENAME_MAX
+ ldy #statvfs::f_namemax
+ sta (ptr4),y
+
+ lda #$00
+ sta ___errno
+ tax
+ rts
+
+ .bss
+
+vol_sep:.res 1
diff --git a/libsrc/apple2/targetutil/convert.c b/libsrc/apple2/targetutil/convert.c
index ea9273fc3..52dffa745 100644
--- a/libsrc/apple2/targetutil/convert.c
+++ b/libsrc/apple2/targetutil/convert.c
@@ -108,7 +108,7 @@ static unsigned get_dir_entry(char* p_name)
}
/* Field header_pointer directly follows field last_mod */
- cur_addr = *(unsigned*)(&dirent->d_mtime.hour + 1);
+ cur_addr = *(unsigned*)(&dirent->d_mtime.time.hour + 1);
dhandle = dio_open(getcurrentdevice());
if (!dhandle) {
diff --git a/libsrc/apple2/tgi/a2.hi.s b/libsrc/apple2/tgi/a2.hi.s
index aeb24f6be..27c494421 100644
--- a/libsrc/apple2/tgi/a2.hi.s
+++ b/libsrc/apple2/tgi/a2.hi.s
@@ -83,7 +83,10 @@ Y2 := ptr4
.byte $74, $67, $69 ; "tgi"
.byte TGI_API_VERSION ; TGI API version number
+
+libref:
.addr $0000 ; Library reference
+
.word 280 ; X resolution
.word 192 ; Y resolution
.byte 8 ; Number of drawing colors
@@ -120,6 +123,10 @@ pages: .byte 2 ; Number of screens available
.bss
+.ifndef __APPLE2ENH__
+machinetype: .res 1
+.endif
+
; Absolute variables used in the code
ERROR: .res 1 ; Error code
@@ -146,13 +153,22 @@ FONT:
; most of the time.
; Must set an error code: NO
INSTALL:
- .ifdef __APPLE2ENH__
+ .ifndef __APPLE2ENH__
+ lda libref
+ ldx libref+1
+ sta ptr1
+ stx ptr1+1
+ ldy #$0
+ lda (ptr1),y
+ sta machinetype
+ bpl :+
+ .endif
; No page switching if 80 column store is enabled
bit RD80COL
bpl :+
lda #$01
sta pages
-: .endif
+:
; Fall through
@@ -175,10 +191,16 @@ INIT:
; Switch into graphics mode
bit MIXCLR
bit HIRES
- .ifdef __APPLE2ENH__
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl clr_txt
+ .endif
+
sta IOUDISON
bit DHIRESOFF
- .endif
+
+clr_txt:
bit TXTCLR
; Beagle Bros Shape Mechanic fonts don't
@@ -200,11 +222,14 @@ DONE:
bit TXTSET
bit LOWSCR
- .ifdef __APPLE2ENH__
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl reset_wndtop
+ .endif
; Limit SET80COL-HISCR to text
bit LORES
- .endif
+reset_wndtop:
; Reset the text window top
lda #$00
sta WNDTOP
@@ -342,7 +367,7 @@ GETPIXEL:
lda #$03 ; 3 (white)
: bcc :+
adc #$03 ; += 4 (black -> black2, white -> white2)
-: ldx #$00
+: ldx #>$0000
bit $C080 ; Switch in LC bank 2 for R/O
rts
diff --git a/libsrc/apple2/tgi/a2.lo.s b/libsrc/apple2/tgi/a2.lo.s
index 6d1c6aa4a..b0f608e86 100644
--- a/libsrc/apple2/tgi/a2.lo.s
+++ b/libsrc/apple2/tgi/a2.lo.s
@@ -53,7 +53,7 @@ Y2 := ptr4
.byte $74, $67, $69 ; "tgi"
.byte TGI_API_VERSION ; TGI API version number
- .addr $0000 ; Library reference
+libref: .addr $0000 ; Library reference
.word 40 ; X resolution
.word 48 ; Y resolution
.byte 16 ; Number of drawing colors
@@ -93,6 +93,10 @@ Y2 := ptr4
ERROR: .res 1 ; Error code
MIX: .res 1 ; 4 lines of text
+.ifndef __APPLE2ENH__
+machinetype: .res 1
+.endif
+
; ------------------------------------------------------------------------
.rodata
@@ -126,11 +130,15 @@ INIT:
bit $C082 ; Switch in ROM
jsr SETGR
bit MIXCLR
- .ifdef __APPLE2ENH__
+
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl lc_in
+ .endif
+
sta IOUDISON
bit DHIRESOFF
- .endif
- bit $C080 ; Switch in LC bank 2 for R/O
+lc_in: bit $C080 ; Switch in LC bank 2 for R/O
; Done, reset the error code
lda #TGI_ERR_OK
@@ -144,6 +152,16 @@ INIT:
; most of the time.
; Must set an error code: NO
INSTALL:
+ .ifndef __APPLE2ENH__
+ lda libref
+ ldx libref+1
+ sta ptr1
+ stx ptr1+1
+ ldy #$0
+ lda (ptr1),y
+ sta machinetype
+ bpl :+
+ .endif
; Fall through
; UNINSTALL routine. Is called before the driver is removed from memory. May
@@ -321,7 +339,7 @@ GETPIXEL:
jsr SCRN
tax
lda COL2TGI,x
- ldx #$00
+ ldx #>$0000
bit $C080 ; Switch in LC bank 2 for R/O
rts
diff --git a/libsrc/apple2/tgiref.s b/libsrc/apple2/tgiref.s
new file mode 100644
index 000000000..e9bcab5e8
--- /dev/null
+++ b/libsrc/apple2/tgiref.s
@@ -0,0 +1,18 @@
+;
+; Colin Leroy-Mira, 2025-05-10
+;
+
+ .export tgi_libref
+ .import _exit
+
+.ifndef __APPLE2ENH__
+
+ .import machinetype
+
+tgi_libref := machinetype
+
+.else
+
+tgi_libref := _exit
+
+.endif
diff --git a/libsrc/apple2/uppercasemask.s b/libsrc/apple2/uppercasemask.s
new file mode 100644
index 000000000..cd818c5bf
--- /dev/null
+++ b/libsrc/apple2/uppercasemask.s
@@ -0,0 +1,27 @@
+;
+; Oliver Schmidt, 2024-08-06
+;
+
+.ifndef __APPLE2ENH__
+
+ .export uppercasemask
+
+ .import machinetype
+ .constructor detectlowercase
+
+ .segment "ONCE"
+
+detectlowercase:
+ bit machinetype
+ bpl :+
+
+ lda #$FF
+ sta uppercasemask
+: rts
+
+
+ .data
+
+uppercasemask: .byte $DF ; Convert to uppercase
+
+.endif
diff --git a/libsrc/apple2/videomode.s b/libsrc/apple2/videomode.s
index 13151a48a..ea9eb28df 100644
--- a/libsrc/apple2/videomode.s
+++ b/libsrc/apple2/videomode.s
@@ -1,68 +1,52 @@
;
; Oliver Schmidt, 07.09.2009
;
-; unsigned __fastcall__ videomode (unsigned mode);
+; signed char __fastcall__ videomode (unsigned mode);
;
- .ifdef __APPLE2ENH__
-
.export _videomode
- .import COUT
+
+ .import aux80col
+ .import returnFFFF
.include "apple2.inc"
+
+VIDEOMODE_40x24 = $15
+VIDEOMODE_80x24 = $00
+
.segment "LOWCODE"
_videomode:
+ bit aux80col
+ bmi set_mode
+
+ ; No 80 column card, return error if requested mode is 80cols
+ cmp #VIDEOMODE_40x24
+ beq out
+ jmp returnFFFF
+set_mode:
+
; Get and save current videomode flag
bit RD80VID
php
- ; If we are in 80 column mode then the 80 column firmware is
- ; known to be active so we can just print the ctrl-char code
- ; (even if this only means staying in the current videomode)
- bpl :+
- jsr COUT
- bra done
-
- ; If we are in 40 column mode and want to set 40 column mode
- ; then we explicitly do nothing as we neither know about the
- ; current state of the 80 column firmware nor want to fix it
-: cmp #$11 ; Ctrl-char code for 40 cols
- beq done
-
- ; If we are in 40 column mode and want to set 80 column mode
- ; then we first presume the 80 column firmware being already
- ; active and print the ctrl-char code (this causes a garbage
- ; char to be printed on the screen if isn't already active)
- jsr COUT
-
- ; If we successfully switched to 80 column mode then the 80
- ; column firmware was in fact already active and we're done
- bit RD80VID
- bmi done
-
- ; The 80 column firmware isn't already active so we need to
- ; initialize it - causing the screen to be cleared and thus
- ; the garbage char printed above to be erased (but for some
- ; reason the cursor horizontal position not to be zeroed)
- stz CH
-
; Initializing the 80 column firmware needs the ROM switched
; in, otherwise it would copy the F8 ROM to the LC (@ $CEF4)
bit $C082
- ; Initialize 80 column firmware
- jsr $C300 ; PR#3
+ ; Call 80 column firmware with ctrl-char code
+ jsr $C300
; Switch in LC bank 2 for R/O
bit $C080
+ ; Switch in alternate charset again
+ sta SETALTCHAR
+
; Return ctrl-char code for setting previous
; videomode using the saved videomode flag
-done: lda #$11 ; Ctrl-char code for 40 cols
+ lda #VIDEOMODE_40x24
plp
- bpl :+
- inc a ; Ctrl-char code for 80 cols
-: rts ; X was preserved all the way
-
- .endif ; __APPLE2ENH__
+ bpl out
+ lda #VIDEOMODE_80x24
+out: rts ; X was preserved all the way
diff --git a/libsrc/apple2/wait.s b/libsrc/apple2/wait.s
new file mode 100644
index 000000000..3b569215b
--- /dev/null
+++ b/libsrc/apple2/wait.s
@@ -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
diff --git a/libsrc/apple2/waitvsync.s b/libsrc/apple2/waitvsync.s
index a4ab5ebb3..d02071915 100644
--- a/libsrc/apple2/waitvsync.s
+++ b/libsrc/apple2/waitvsync.s
@@ -3,28 +3,26 @@
;
; void waitvsync (void);
;
- .ifdef __APPLE2ENH__
-
- .constructor initvsync
.export _waitvsync
- .import _get_ostype
+ .import ostype
+
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
.include "apple2.inc"
- .segment "ONCE"
-
-initvsync:
- jsr _get_ostype
- sta ostype
- rts
-
- .code
-
_waitvsync:
+ .ifndef __APPLE2ENH__
+ bit machinetype ; IIe/enh?
+ bpl out ; No, silently fail
+ .endif
+
bit ostype
bmi iigs ; $8x
bvs iic ; $4x
+ ; Apple IIe
: bit RDVBLBAR
bpl :- ; Blanking
: bit RDVBLBAR
@@ -39,7 +37,8 @@ iigs: bit RDVBLBAR
rts
; Apple IIc TechNote #9, Detecting VBL
-iic: sei
+iic: php
+ sei
sta IOUDISOFF
lda RDVBLMSK
bit ENVBL
@@ -50,11 +49,5 @@ iic: sei
bcs :+ ; VBL interrupts were already enabled
bit DISVBL
: sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on.
- cli
- rts
-
- .segment "INIT"
-
-ostype: .res 1
-
- .endif ; __APPLE2ENH__
+ plp
+out: rts
diff --git a/libsrc/apple2/wherex.s b/libsrc/apple2/wherex.s
index bd717a22b..b447ac6e9 100644
--- a/libsrc/apple2/wherex.s
+++ b/libsrc/apple2/wherex.s
@@ -4,11 +4,23 @@
; unsigned char wherex (void);
;
+ .ifndef __APPLE2ENH__
+ .import machinetype
+ .endif
+
.export _wherex
.include "apple2.inc"
_wherex:
lda CH
- ldx #$00
+ .ifndef __APPLE2ENH__
+ bit machinetype
+ bpl :+
+ .endif
+ bit RD80VID ; In 80 column mode?
+ bpl :+
+ lda OURCH
+
+: ldx #>$0000
rts
diff --git a/libsrc/apple2/wherey.s b/libsrc/apple2/wherey.s
index daacaaba7..a3843f606 100644
--- a/libsrc/apple2/wherey.s
+++ b/libsrc/apple2/wherey.s
@@ -12,5 +12,5 @@ _wherey:
lda CV
sec
sbc WNDTOP
- ldx #$00
+ ldx #>$0000
rts
diff --git a/libsrc/apple2/write.s b/libsrc/apple2/write.s
index 7b50d0705..5fb51cca6 100644
--- a/libsrc/apple2/write.s
+++ b/libsrc/apple2/write.s
@@ -7,6 +7,9 @@
.export _write
.import rwprolog, rwcommon, rwepilog
.import COUT
+ .ifndef __APPLE2ENH__
+ .import uppercasemask
+ .endif
.include "zeropage.inc"
.include "errno.inc"
@@ -84,7 +87,7 @@ next: lda (ptr1),y
.ifndef __APPLE2ENH__
cmp #$E0 ; Test for lowercase
bcc output
- and #$DF ; Convert to uppercase
+ and uppercasemask
.endif
output: jsr COUT ; Preserves X and Y
diff --git a/libsrc/atari/cashdr.s b/libsrc/atari/cashdr.s
index 99aefe68f..c0d64a56b 100644
--- a/libsrc/atari/cashdr.s
+++ b/libsrc/atari/cashdr.s
@@ -10,10 +10,10 @@
.include "atari.inc"
- .import __BSS_RUN__, __STARTADDRESS__, _cas_init
+ .import __INIT_RUN__, __STARTADDRESS__, _cas_init
.export _cas_hdr
-.assert ((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128) < $101, error, "File to big to load from cassette"
+.assert ((__INIT_RUN__ - __STARTADDRESS__ + 127) / 128) < $101, error, "File to big to load from cassette"
; for a description of the cassette header, see De Re Atari, appendix C
@@ -22,7 +22,7 @@
_cas_hdr:
.byte 0 ; ignored
- .byte <((__BSS_RUN__ - __STARTADDRESS__ + 127) / 128) ; # of 128-byte records to read
+ .byte <((__INIT_RUN__ - __STARTADDRESS__ + 127) / 128) ; # of 128-byte records to read
.word __STARTADDRESS__ ; load address
.word _cas_init ; init address
@@ -31,6 +31,8 @@ _cas_hdr:
ldy #80
sta (SAVMSC),y
.endif
+ lda #$3c ; motor off
+ sta PACTL
clc
rts
diff --git a/libsrc/atari/cpeekc.s b/libsrc/atari/cpeekc.s
new file mode 100644
index 000000000..a51e477a6
--- /dev/null
+++ b/libsrc/atari/cpeekc.s
@@ -0,0 +1,35 @@
+;
+; 2016-02-28, Groepaz
+; 2017-06-21, Greg King
+;
+; char cpeekc (void);
+;
+
+ .export _cpeekc
+
+ .include "atari.inc"
+
+
+_cpeekc:
+ lda OLDCHR ; get char under cursor
+ and #<~$80 ; remove reverse bit
+
+ ;; convert internal screen code to AtSCII
+
+ tay
+ and #%01100000
+ asl a
+ asl a
+ rol a
+ rol a
+ tax
+ tya
+ eor intats,x
+ ldx #>$0000
+ rts
+
+ .rodata
+intats: .byte %00100000 ; -> %001xxxxx
+ .byte %01100000 ; -> %010xxxxx
+ .byte %01000000 ; -> %000xxxxx
+ .byte %00000000 ; -> %011xxxxx
diff --git a/libsrc/atari/cpeekcolor.s b/libsrc/atari/cpeekcolor.s
new file mode 100644
index 000000000..ed275ec95
--- /dev/null
+++ b/libsrc/atari/cpeekcolor.s
@@ -0,0 +1,8 @@
+;
+; 2017-06-03, Greg King
+;
+; unsigned char cpeekcolor (void);
+;
+
+ .import return1
+ .export _cpeekcolor := return1 ; always COLOR_WHITE
diff --git a/libsrc/atari/cpeekrevers.s b/libsrc/atari/cpeekrevers.s
new file mode 100644
index 000000000..15b26fcd4
--- /dev/null
+++ b/libsrc/atari/cpeekrevers.s
@@ -0,0 +1,18 @@
+;
+; 2017-06-21, Greg King
+;
+; unsigned char cpeekrevers (void);
+;
+
+ .export _cpeekrevers
+
+ .include "atari.inc"
+
+
+_cpeekrevers:
+ lda OLDCHR ; get char under cursor
+ and #$80 ; get reverse bit
+ asl a
+ tax ; ldx #>$0000
+ rol a ; return boolean value
+ rts
diff --git a/libsrc/atari/crt0.s b/libsrc/atari/crt0.s
index 381aa699f..50e3ca7b6 100644
--- a/libsrc/atari/crt0.s
+++ b/libsrc/atari/crt0.s
@@ -204,6 +204,10 @@ APPMHI_save: .res 2
; ------------------------------------------------------------------------
+.segment "INIT" ; have at least one (empty) segment of INIT, exehdr.s needs its definition
+
+; ------------------------------------------------------------------------
+
.segment "LOWCODE" ; have at least one (empty) segment of LOWCODE, so that the next line works even if the program doesn't make use of this segment
.assert (__LOWCODE_RUN__ + __LOWCODE_SIZE__ <= $4000 || __LOWCODE_RUN__ > $7FFF || __LOWCODE_SIZE__ = 0), warning, "'lowcode area' reaches into $4000..$7FFF bank memory window"
; check for LOWBSS_SIZE = 0 not needed since the only file which uses LOWBSS (irq.s) also uses LOWCODE
diff --git a/libsrc/atari/exehdr.s b/libsrc/atari/exehdr.s
index 7abb7c1ac..1ac9a0fbe 100644
--- a/libsrc/atari/exehdr.s
+++ b/libsrc/atari/exehdr.s
@@ -1,11 +1,15 @@
; This file defines the EXE header and main chunk load header for Atari executables
.export __EXEHDR__: absolute = 1
- .import __MAIN_START__, __BSS_LOAD__
+ .import __MAIN_START__, __INIT_LOAD__
.segment "EXEHDR"
.word $FFFF
.segment "MAINHDR"
.word __MAIN_START__
- .word __BSS_LOAD__ - 1
+ .word __INIT_LOAD__ - 1
+
+; Define the INIT segment so that __INIT_LOAD__ from above '.import' is always defined.
+; The segment is normally present when linking a C program, but not necessarily when linking an assembler program.
+.segment "INIT"
diff --git a/libsrc/atari/getdevice.s b/libsrc/atari/getdevice.s
index e0e700436..b98d3ad5f 100644
--- a/libsrc/atari/getdevice.s
+++ b/libsrc/atari/getdevice.s
@@ -50,7 +50,7 @@ check_device:
lda #SIO_STAT
sta DCOMND ; set command into DCB
lda #%01000000 ; direction value, "receive data"
- sta DSTATS ; set data flow directon
+ sta DSTATS ; set data flow direction
lda #15
sta DTIMLO ; value got from DOS source
lda #4
diff --git a/libsrc/atari/graphics.s b/libsrc/atari/graphics.s
index 1f7844c39..8b4e8034d 100644
--- a/libsrc/atari/graphics.s
+++ b/libsrc/atari/graphics.s
@@ -23,7 +23,7 @@
.code
-; set new grapics mode
+; set new graphics mode
; gets new mode in A
; returns handle or -1 on error
; uses tmp1, tmp2, tmp3, tmp4 (in subroutines)
diff --git a/libsrc/atari/mou/atrtt.s b/libsrc/atari/mou/atrtt.s
index b1e53935e..f7c56e9f2 100644
--- a/libsrc/atari/mou/atrtt.s
+++ b/libsrc/atari/mou/atrtt.s
@@ -374,7 +374,7 @@ IRQ:
; The touch pad is read thru the paddle potentiometers. The possible
; values are 1..228. Since the maximum value is less than the X
; dimension we have to "stretch" this value. In order to use only
-; divisions by powers of two, we use the following appoximation:
+; divisions by powers of two, we use the following approximation:
; 320/227 = 1.4096
; 1+1/2-1/8+1/32 = 1.4062
; For Y we subtract 1/8 of it to get in the YMax ballpark.
@@ -385,7 +385,7 @@ IRQ:
; X
- ldx PADDL0 ; get X postion
+ ldx PADDL0 ; get X position
dex ; decrement, since it's 1-based
stx XPos
txa
@@ -445,7 +445,7 @@ IRQ:
; Y
- ldx PADDL1 ; get Y postion
+ ldx PADDL1 ; get Y position
dex ; decrement, since it's 1-based
stx YPos
lda #228
diff --git a/libsrc/atari/open.s b/libsrc/atari/open.s
index ed3e40b2f..e7e55c54c 100644
--- a/libsrc/atari/open.s
+++ b/libsrc/atari/open.s
@@ -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 #SCREEN_BUF
sta SAVMSC+1
- ; initialize cursor position
- lda #0
- sta COLCRS_5200
- sta ROWCRS_5200
-
; clear screen buffer
- ldy #<(SCREEN_BUF_SIZE-1)
- ldx #>(SCREEN_BUF_SIZE-1)
-clrscr: sta (SAVMSC),y
- dey
- cpy #$FF
- bne clrscr
- dex
- cpx #$FF
- bne clrscr
+ jsr _clrscr
; set default colors
-
lda #GTIA_COLOR_WHITE
sta COLOR0
lda #GTIA_COLOR_LIGHTRED
@@ -55,7 +42,6 @@ clrscr: sta (SAVMSC),y
sta COLOR4 ; background
; set display list
-
lda #dlist
@@ -82,7 +68,7 @@ dlist: .repeat 3
.byte DL_CHR20x8x2
.endrepeat
- .byte DL_JVB
+ .byte DL_JVB
.word dlist
; end of display list
diff --git a/libsrc/atari5200/extra/conioscreen-20x12.s b/libsrc/atari5200/extra/conioscreen-20x12.s
index aeb11cb43..c2aa0a65f 100644
--- a/libsrc/atari5200/extra/conioscreen-20x12.s
+++ b/libsrc/atari5200/extra/conioscreen-20x12.s
@@ -7,6 +7,7 @@
SCREEN_BUF_SIZE = 20 * 12
SCREEN_BUF = $4000 - SCREEN_BUF_SIZE
+ .import _clrscr
.export screen_setup
.export screen_width, screen_height
.export conio_color
@@ -26,24 +27,10 @@ screen_setup:
lda #>SCREEN_BUF
sta SAVMSC+1
- ; initialize cursor position
- lda #0
- sta COLCRS_5200
- sta ROWCRS_5200
-
; clear screen buffer
- ldy #<(SCREEN_BUF_SIZE-1)
- ldx #>(SCREEN_BUF_SIZE-1)
-clrscr: sta (SAVMSC),y
- dey
- cpy #$FF
- bne clrscr
- dex
- cpx #$FF
- bne clrscr
+ jsr _clrscr
; set default colors
-
lda #GTIA_COLOR_WHITE
sta COLOR0
lda #GTIA_COLOR_LIGHTRED
@@ -55,7 +42,6 @@ clrscr: sta (SAVMSC),y
sta COLOR4 ; background
; set display list
-
lda #dlist
@@ -82,7 +68,7 @@ dlist: .repeat 3
.byte DL_CHR20x16x2
.endrepeat
- .byte DL_JVB
+ .byte DL_JVB
.word dlist
; end of display list
diff --git a/libsrc/atari7800/joy/atari7800-stdjoy.s b/libsrc/atari7800/joy/atari7800-stdjoy.s
index 59f656ada..c24e87e29 100644
--- a/libsrc/atari7800/joy/atari7800-stdjoy.s
+++ b/libsrc/atari7800/joy/atari7800-stdjoy.s
@@ -53,13 +53,20 @@ JOY_COUNT = 2 ; Number of joysticks we support
; Must return an JOY_ERR_xx code in a/x.
;
+PB2 = $04 ; Joystick 0
+PB4 = $10 ; Joystick 1
+
INSTALL:
; Assume 7800 2-button controller, can change
; to 2600 1-button later
- lda #$14
- sta CTLSWB ; enable 2-button 7800 controller 1: set pin 6 to output
+ lda #(PB2 | PB4)
+ ; enable 2-button 7800 controllers on both ports
+ ; by setting PB2 and PB4 to output
+ sta CTLSWB
+ ; enable 2-button 7800 controllers by setting
+ ; the outputs to 0; (INPT4 and INPT5) high
ldy #$00
- sty SWCHB ; enable 2-button 7800 controller 2: pull pin 6 (INPT4) high
+ sty SWCHB
reset:
lda #JOY_ERR_OK
@@ -88,6 +95,28 @@ COUNT:
; ------------------------------------------------------------------------
; READ: Read a particular joystick passed in A for 2 fire buttons.
+readdualbuttons0:
+ ldy #0 ; ........
+ bit INPT0 ; Check for right button
+ bpl L1
+ ldy #2 ; ......2.
+L1: bit INPT1 ; Check for left button
+ bpl L2
+ iny ; ......21
+L2: tya
+ rts
+
+readdualbuttons1:
+ ldy #0 ; ........
+ bit INPT2 ; Check for right button
+ bpl L1
+ ldy #2 ; ......2.
+L3: bit INPT3 ; Check for left button
+ bpl L2
+ iny ; ......21
+L4: tya
+ rts
+
readbuttons:
; Y has joystick of interest 0/1
; return value:
@@ -97,42 +126,48 @@ readbuttons:
; $03: both buttons
; preserves X
tya
- beq L5
+ beq readbuttons0
+readbuttons1:
; Joystick 1 processing
- ; 7800 joystick 1 buttons
- ldy #0 ; ........
- bit INPT2 ; Check for right button
- bpl L1
- ldy #2 ; ......2.
-L1: bit INPT3 ;Check for left button
- bpl L2
- iny ; ......21
-L2: tya
- bne L4 ; 7800 mode joystick worked
- ; 2600 Joystick 1
+ ; Start by checking for single button 2600 joystick
bit INPT5
- bmi L4
-L3: iny ; .......1
- lda #0 ; Fallback to 2600 joystick mode
- sta CTLSWB
-L4: tya ; ......21
+ bpl singlebtn1detected
+ jmp readdualbuttons1
+singlebtn1detected:
+ ; Single button joystick detected but could be dual
+ jsr readdualbuttons1
+ bne L5 ; It was a dual button press
+ ; It was a single button press
+ bit INPT5
+ bmi L5
+ iny ; .......1
+ lda #PB4 ; Joystick 1 is a single button unit
+ clc
+ adc SWCHB
+ sta SWCHB ; Cut power from the dual button circuit
+L5: tya ; ......21
rts
-L5: ; Joystick 0 processing
- ; 7800 joystick 0 buttons
- ldy #0 ; ........
- bit INPT0 ; Check for right button
- bpl L6
- ldy #2 ; ......2.
-L6: bit INPT1 ;Check for left button
- bpl L7
- iny ; ......21
-L7: tya
- bne L4 ; 7800 mode joystick worked
- ; 2600 Joystick 0
+readbuttons0:
+ ; Joystick 0 processing
+ ; Start by checking for single button 2600 joystick
bit INPT4
- bmi L4
- bpl L3
+ bpl singlebtn0detected
+ jmp readdualbuttons0
+singlebtn0detected:
+ ; Single button joystick detected but could be dual
+ jsr readdualbuttons0
+ bne L6 ; It was a dual button press
+ ; It was a single button press
+ bit INPT4
+ bmi L6
+ iny ; .......1
+ lda #PB2 ; Joystick 0 is a single button unit
+ clc
+ adc SWCHB
+ sta SWCHB ; Cut power from the dual button circuit
+L6: tya ; ......21
+ rts
READ:
tay ; Store joystick 0/1 in Y
diff --git a/libsrc/atmos/cpeekc.s b/libsrc/atmos/cpeekc.s
new file mode 100644
index 000000000..f0f5f99b1
--- /dev/null
+++ b/libsrc/atmos/cpeekc.s
@@ -0,0 +1,21 @@
+;
+; 2016-02-28, Groepaz
+; 2017-06-19, Greg King
+;
+; char cpeekc (void);
+;
+; Atmos version
+;
+
+ .export _cpeekc
+
+ .import setscrptr
+ .importzp ptr2
+
+
+_cpeekc:
+ jsr setscrptr ; Set ptr2 and .Y to the cursor's address
+ lda (ptr2),y ; Get char
+ and #<~$80 ; Remove revers() bit
+ ldx #>$0000
+ rts
diff --git a/libsrc/atmos/cpeekcolor.s b/libsrc/atmos/cpeekcolor.s
new file mode 100644
index 000000000..307c04e96
--- /dev/null
+++ b/libsrc/atmos/cpeekcolor.s
@@ -0,0 +1,10 @@
+;
+; 2017-06-03, Greg King
+;
+; unsigned char cpeekcolor (void);
+;
+; Atmos version
+;
+
+ .import return1
+ .export _cpeekcolor := return1 ; always COLOR_WHITE
diff --git a/libsrc/atmos/cpeekrevers.s b/libsrc/atmos/cpeekrevers.s
new file mode 100644
index 000000000..53f907511
--- /dev/null
+++ b/libsrc/atmos/cpeekrevers.s
@@ -0,0 +1,22 @@
+;
+; 2017-06-08, Greg King
+;
+; unsigned char cpeekrevers (void);
+;
+; Atmos version
+;
+
+ .export _cpeekrevers
+
+ .import setscrptr
+ .importzp ptr2
+
+
+_cpeekrevers:
+ jsr setscrptr ; Set ptr2 and .Y to the cursor's address
+ lda (ptr2),y ; Get char
+ and #$80 ; get reverse bit
+ asl a
+ tax ; ldx #>$0000
+ rol a ; return boolean value
+ rts
diff --git a/libsrc/atmos/cpeeks.s b/libsrc/atmos/cpeeks.s
new file mode 100644
index 000000000..e54563e7d
--- /dev/null
+++ b/libsrc/atmos/cpeeks.s
@@ -0,0 +1,54 @@
+;
+; 2017-06-20, Greg King
+;
+; void cpeeks (char* s, unsigned length);
+;
+
+ .export _cpeeks
+
+ .import setscrptr, popax
+ .importzp ptr1, ptr2, ptr3, tmp1, tmp2
+
+ .macpack generic
+
+
+_cpeeks:
+ eor #<$FFFF ; counting a word upward is faster
+ sta ptr3 ; so, we use -(length + 1)
+ txa
+ eor #>$FFFF
+ sta ptr3+1
+
+ jsr setscrptr ; Set ptr2 and .Y to the cursor's address
+ sty tmp2
+
+ jsr popax
+ sta tmp1 ; (will be a .Y index)
+ stx ptr1+1
+ ldx #<$0000
+ stx ptr1
+ bze L3 ; branch always
+
+L4: ldy tmp2
+ lda (ptr2),y ; Get char
+ iny
+ bnz L2
+ inc ptr2+1
+L2: sty tmp2
+ and #<~$80 ; Remove reverse bit
+ ldy tmp1
+ sta (ptr1),y
+ iny
+ bnz L1
+ inc ptr1+1
+L1: sty tmp1
+
+L3: inc ptr3 ; count length
+ bnz L4
+ inc ptr3+1
+ bnz L4
+
+ txa ; terminate the string
+ ldy tmp1
+ sta (ptr1),y
+ rts
diff --git a/libsrc/atmos/waitvsync.s b/libsrc/atmos/waitvsync.s
new file mode 100644
index 000000000..85e50a795
--- /dev/null
+++ b/libsrc/atmos/waitvsync.s
@@ -0,0 +1,18 @@
+;
+; Written by Stefan Haubenthal , requires VSync hack
+;
+; void waitvsync (void);
+;
+
+ .export _waitvsync
+
+ .include "atmos.inc"
+
+.proc _waitvsync
+
+ lda #%00010000 ; CB1
+wait: and VIA::PRA2
+ bne wait
+ rts
+
+.endproc
diff --git a/libsrc/c128/break.s b/libsrc/c128/break.s
index 092ca3469..450183dc3 100644
--- a/libsrc/c128/break.s
+++ b/libsrc/c128/break.s
@@ -125,11 +125,10 @@ uservec: jmp $FFFF ; Patched at runtime
.data
-; Old break vector preceeded by a jump opcode
+; Old break vector preceded by a jump opcode
brk_old:
jmp $0000
-; Indirect vectors preceeded by a jump opcode
+; Indirect vectors preceded by a jump opcode
brk_ind:
jmp $0000
-
diff --git a/libsrc/c128/ser/c128-swlink.s b/libsrc/c128/ser/c128-swlink.s
index b8f08159b..796dc3b3b 100644
--- a/libsrc/c128/ser/c128-swlink.s
+++ b/libsrc/c128/ser/c128-swlink.s
@@ -134,7 +134,7 @@ ParityTable:
; Interrupt stub that is copied into low RAM. The startup code uses a special
; memory configuration with just kernal and I/O enabled (anything else is RAM).
; The NMI handler in ROM will switch back to a configuration where just the
-; low 16K RAM are accessible. So we have to copy a smal piece of code into
+; low 16K RAM are accessible. So we have to copy a small piece of code into
; low RAM that enables the cc65 configuration and then jumps to the real NMI
; handler.
@@ -296,7 +296,7 @@ SER_CLOSE:
lda #%00001010
sta ACIA_CMD
-; Initalize buffers. Returns zero in a
+; Initialize buffers. Returns zero in a
jsr InitBuffers
diff --git a/libsrc/c128/tgi/c128-vdc.s b/libsrc/c128/tgi/c128-vdc.s
index f48b530f6..edbdd6cd8 100644
--- a/libsrc/c128/tgi/c128-vdc.s
+++ b/libsrc/c128/tgi/c128-vdc.s
@@ -268,7 +268,7 @@ INIT:
@L1: ldx #$FF
stx BITMASK
-; Remeber current color value
+; Remember current color value
ldx #VDC_COLORS
jsr VDCReadReg
sta OLDCOLOR
diff --git a/libsrc/c128/tgi/c128-vdc2.s b/libsrc/c128/tgi/c128-vdc2.s
index 4b7b17c57..6b75ee712 100644
--- a/libsrc/c128/tgi/c128-vdc2.s
+++ b/libsrc/c128/tgi/c128-vdc2.s
@@ -277,7 +277,7 @@ INIT:
@L1: ldx #$FF
stx BITMASK
-; Remeber current color value
+; Remember current color value
ldx #VDC_COLORS
jsr VDCReadReg
sta OLDCOLOR
diff --git a/libsrc/c128/waitvsync.s b/libsrc/c128/waitvsync.s
index e4bbbf7c9..573f574a7 100644
--- a/libsrc/c128/waitvsync.s
+++ b/libsrc/c128/waitvsync.s
@@ -23,8 +23,8 @@ _waitvsync:
@c80:
;FIXME: do we have to switch banks?
+ lda #$20
@l3:
- lda VDC_INDEX
- and #$20
+ and VDC_INDEX
beq @l3
rts
diff --git a/libsrc/c64/emd/c64-rrr.s b/libsrc/c64/emd/c64-rrr.s
index 4d175cd32..8632e5e64 100644
--- a/libsrc/c64/emd/c64-rrr.s
+++ b/libsrc/c64/emd/c64-rrr.s
@@ -212,8 +212,8 @@ MAP:
cmp pagecount
bcs return_null
sta curpage
- lda #dummy ; adress in .A/.X)
+ lda #dummy ; address in .A/.X)
jsr COPYFROM
bcs return_win ; function returns pointer to window (returns always with carry set!)
@@ -224,8 +224,8 @@ COMMIT:
lda curpage
cmp pagecount
bcs return
- lda #dummy ; adress in .A/.X)
+ lda #dummy ; address in .A/.X)
;----------------------------------------------------------------------------------------
;void __fastcall__ em_copyto (struct em_copy *copy_data);
@@ -324,7 +324,7 @@ get_struct_data:
;read and process the values from the em_copy struct passed to as parameters rameter to the
;functions em_copyto and em_copyfrom
- sta aux ;store adress of struct (passed in .A/.X) into a zp pointer
+ sta aux ;store address of struct (passed in .A/.X) into a zp pointer
stx aux+1
ldy #0 ;index 0
@@ -347,7 +347,7 @@ get_struct_data:
lsr
lsr ;shift into bits 3 and 4
ora #$23 ;set bit 5 (select ram) and 1+2 (game/exrom setting for ULTIMAX-mode)
- tax ;.X has now the value to write into $de00 to acess rr-ram at desired 16k-bank
+ tax ;.X has now the value to write into $de00 to access rr-ram at desired 16k-bank
iny
iny ;skip unused byte
lda (aux),y ;read length lo-byte
@@ -357,7 +357,7 @@ get_struct_data:
iny
lda (aux),y ;length hi-byte
adc c64_ram+1
- sta len+1 ;tmp2: length, tmp3 contains end adress of transfer in c64-ram.
+ sta len+1 ;tmp2: length, tmp3 contains end address of transfer in c64-ram.
rts
;55 bytes
diff --git a/libsrc/c64/joy/c64-hitjoy.s b/libsrc/c64/joy/c64-hitjoy.s
index a9d454fd0..c57f45780 100644
--- a/libsrc/c64/joy/c64-hitjoy.s
+++ b/libsrc/c64/joy/c64-hitjoy.s
@@ -102,7 +102,7 @@ readadapter:
lda #%00010001
sta $dd0e ; control register a
; timer: start
- ; continous
+ ; continuous
; forced load
; serial port: input
@@ -110,7 +110,7 @@ readadapter:
lda #%01010001
sta $dc0e ; control register a
; timer: start
- ; continous
+ ; continuous
; forced load
; serial port: output
diff --git a/libsrc/c64/ser/c64-swlink.s b/libsrc/c64/ser/c64-swlink.s
index 067a7ca92..2c06bb39b 100644
--- a/libsrc/c64/ser/c64-swlink.s
+++ b/libsrc/c64/ser/c64-swlink.s
@@ -270,7 +270,7 @@ SER_CLOSE:
lda #%00001010
sta ACIA_CMD
-; Initalize buffers. Returns zero in a
+; Initialize buffers. Returns zero in a
jsr InitBuffers
diff --git a/libsrc/cbm/cbm_read.s b/libsrc/cbm/cbm_read.s
index 8a9939eca..c449567b1 100644
--- a/libsrc/cbm/cbm_read.s
+++ b/libsrc/cbm/cbm_read.s
@@ -24,7 +24,7 @@
; /* the kernal routine BASIN sets ST to EOF if the end of file
; ** is reached the first time, then we have store tmp.
; ** every subsequent call returns EOF and READ ERROR in ST, then
-; ** we have to exit the loop here immediatly.
+; ** we have to exit the loop here immediately.
; */
; if (cbm_k_readst() & 0xBF) break;
;
@@ -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
diff --git a/libsrc/cbm/cbm_write.s b/libsrc/cbm/cbm_write.s
index 18c6f4684..a4ecfbe3f 100644
--- a/libsrc/cbm/cbm_write.s
+++ b/libsrc/cbm/cbm_write.s
@@ -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
diff --git a/libsrc/cbm/exec.c b/libsrc/cbm/exec.c
index b9c1bdc96..49ab53553 100644
--- a/libsrc/cbm/exec.c
+++ b/libsrc/cbm/exec.c
@@ -89,7 +89,7 @@ int __fastcall__ exec (const char* progname, const char* cmdline)
}
utoa (dv, basic.unit, 10);
- /* Tape files can be openned only once; skip this test for the Datasette. */
+ /* Tape files can be opened only once; skip this test for the Datasette. */
if (dv != 1) {
/* Don't try to run a program that can't be found. */
fd = open (progname, O_RDONLY);
diff --git a/libsrc/cbm/getdevice.s b/libsrc/cbm/getdevice.s
index 08ba6d5f8..b68e716b4 100644
--- a/libsrc/cbm/getdevice.s
+++ b/libsrc/cbm/getdevice.s
@@ -54,7 +54,7 @@ next: inx
lda ST
-; Either the Kernal calls above were successfull or there was
+; Either the Kernal calls above were successful or there was
; already a cmdchannel to the device open - which is a pretty
; good indication of its existence ;-)
diff --git a/libsrc/cbm/loadaddr.s b/libsrc/cbm/loadaddr.s
index 0675dd67d..4329f201f 100644
--- a/libsrc/cbm/loadaddr.s
+++ b/libsrc/cbm/loadaddr.s
@@ -2,7 +2,7 @@
; Ullrich von Bassewitz, 2010-11-13
;
; This module supplies the load address that is expected by Commodore
-; machines in the first two bytes of an excutable disk file.
+; machines in the first two bytes of an executable disk file.
;
diff --git a/libsrc/cbm510/joy/cbm510-std.s b/libsrc/cbm510/joy/cbm510-std.s
index f7cbb2cdc..640b0d2a8 100644
--- a/libsrc/cbm510/joy/cbm510-std.s
+++ b/libsrc/cbm510/joy/cbm510-std.s
@@ -117,7 +117,7 @@ READ: ldx #$0F ; Switch to the system bank
lsr tmp1
lsr tmp1
-; Mask the relavant bits, get the push button bit
+; Mask the relevant bits, get the push button bit
@L2: asl a ; push button bit into carry
lda tmp1
diff --git a/libsrc/common/_printf.s b/libsrc/common/_printf.s
index a0074583e..d7eeb072d 100644
--- a/libsrc/common/_printf.s
+++ b/libsrc/common/_printf.s
@@ -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
diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s
new file mode 100644
index 000000000..9bcf84184
--- /dev/null
+++ b/libsrc/common/_time_t_to_tm.s
@@ -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
+ jsr _mktime
+
+ ; And return our pointer
+ lda #TM
+ rts
+
+ .bss
+
+TM: .tag tm
diff --git a/libsrc/common/asctime.s b/libsrc/common/asctime.s
new file mode 100644
index 000000000..efcf34b41
--- /dev/null
+++ b/libsrc/common/asctime.s
@@ -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
+ jsr pushax
+
+ ; Push sizeof(buf)
+ lda #MAX_BUF_LEN
+ jsr pushax
+
+ ; Push format string
+ lda #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
+ rts
+
+ .data
+
+fmt: .byte '%'
+ .byte 'c'
+ .byte $0A
+ .byte $00
+
+ .bss
+
+buf: .res MAX_BUF_LEN
diff --git a/libsrc/common/checkferror.s b/libsrc/common/checkferror.s
new file mode 100644
index 000000000..736fa3ccd
--- /dev/null
+++ b/libsrc/common/checkferror.s
@@ -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
diff --git a/libsrc/common/ctype.s b/libsrc/common/ctype.s
index 220ad79c1..d51b1bf2b 100644
--- a/libsrc/common/ctype.s
+++ b/libsrc/common/ctype.s
@@ -7,7 +7,7 @@
;
; See "LICENSE" file for legal information.
;
-; Character specification table, matching serveral consoles.
+; Character specification table, matching several consoles.
;
.include "ctypetable.inc"
diff --git a/libsrc/common/divt.s b/libsrc/common/divt.s
index 7f2b4e1bb..52b6efd04 100644
--- a/libsrc/common/divt.s
+++ b/libsrc/common/divt.s
@@ -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;
diff --git a/libsrc/common/fclose.s b/libsrc/common/fclose.s
index 2368bf9f6..f6c57841e 100644
--- a/libsrc/common/fclose.s
+++ b/libsrc/common/fclose.s
@@ -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
-
diff --git a/libsrc/common/fgetc.c b/libsrc/common/fgetc.c
deleted file mode 100644
index b4ba18d73..000000000
--- a/libsrc/common/fgetc.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
-** fgetc.c
-**
-** (C) Copyright 1998, 2002 Ullrich von Bassewitz (uz@cc65.org)
-**
-*/
-
-
-
-#include
-#include
-#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;
-
- }
-}
-
-
-
diff --git a/libsrc/common/fgetc.s b/libsrc/common/fgetc.s
new file mode 100644
index 000000000..34d4df3aa
--- /dev/null
+++ b/libsrc/common/fgetc.s
@@ -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
+ 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
diff --git a/libsrc/common/fgets.c b/libsrc/common/fgets.c
deleted file mode 100644
index 21a991fd6..000000000
--- a/libsrc/common/fgets.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
-** Ullrich von Bassewitz, 11.08.1998
-**
-** char* fgets (char* s, int size, FILE* f);
-*/
-
-
-
-#include
-#include
-#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;
-}
-
-
-
diff --git a/libsrc/common/fgets.s b/libsrc/common/fgets.s
new file mode 100644
index 000000000..c25664f19
--- /dev/null
+++ b/libsrc/common/fgets.s
@@ -0,0 +1,119 @@
+;
+; 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
+
+ .feature string_escapes
+
+ .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 #buf
+ lda #
-#include
-#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;
-}
-
-
-
diff --git a/libsrc/common/fputc.s b/libsrc/common/fputc.s
new file mode 100644
index 000000000..358723538
--- /dev/null
+++ b/libsrc/common/fputc.s
@@ -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
+ 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
diff --git a/libsrc/common/fputs.c b/libsrc/common/fputs.c
deleted file mode 100644
index be476a3f0..000000000
--- a/libsrc/common/fputs.c
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-** int fputs (const char* s, FILE* f);
-**
-** Ullrich von Bassewitz, 11.08.1998
-*/
-
-
-
-#include
-#include
-#include
-#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));
-}
-
-
-
diff --git a/libsrc/common/fputs.s b/libsrc/common/fputs.s
new file mode 100644
index 000000000..b79a4707f
--- /dev/null
+++ b/libsrc/common/fputs.s
@@ -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
diff --git a/libsrc/common/fread.s b/libsrc/common/fread.s
index 647a7f2c8..b39b9d748 100644
--- a/libsrc/common/fread.s
+++ b/libsrc/common/fread.s
@@ -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
-
diff --git a/libsrc/common/fwrite.s b/libsrc/common/fwrite.s
index 861feb120..e7151da95 100644
--- a/libsrc/common/fwrite.s
+++ b/libsrc/common/fwrite.s
@@ -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
-
diff --git a/libsrc/common/gets.c b/libsrc/common/gets.c
deleted file mode 100644
index 2936c70de..000000000
--- a/libsrc/common/gets.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-** gets.c
-**
-** Ullrich von Bassewitz, 11.08.1998
-*/
-
-
-
-#include
-#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;
-}
-
-
-
-
diff --git a/libsrc/common/gets.s b/libsrc/common/gets.s
new file mode 100644
index 000000000..dfaf2def3
--- /dev/null
+++ b/libsrc/common/gets.s
@@ -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
diff --git a/libsrc/common/gmtime.c b/libsrc/common/gmtime.c
deleted file mode 100644
index 85e9de3d0..000000000
--- a/libsrc/common/gmtime.c
+++ /dev/null
@@ -1,73 +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
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-struct tm* __fastcall__ gmtime (const time_t* timep)
-{
- static struct tm timebuf;
- time_t t;
-
- /* Check the argument */
- if (timep == 0 || (long) (t = *timep) < 0) {
- /* Invalid arg */
- return 0;
- }
-
- /* 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;
-}
diff --git a/libsrc/common/gmtime.s b/libsrc/common/gmtime.s
new file mode 100644
index 000000000..288b285eb
--- /dev/null
+++ b/libsrc/common/gmtime.s
@@ -0,0 +1,20 @@
+;
+; Colin Leroy-Mira, 2024
+;
+; struct tm* __fastcall__ gmtime (const time_t* timep);
+;
+
+ .export _gmtime
+ .import __time_t_to_tm
+ .import ldeaxi
+
+_gmtime:
+ cpx #$00 ; Check for null pointer
+ bne :+
+ cmp #$00
+ beq no_pointer
+: jsr ldeaxi ; Load value from pointer
+ jmp __time_t_to_tm ; Convert it
+
+no_pointer:
+ rts ; A/X already set
diff --git a/libsrc/common/localtime.s b/libsrc/common/localtime.s
new file mode 100644
index 000000000..279442c9d
--- /dev/null
+++ b/libsrc/common/localtime.s
@@ -0,0 +1,29 @@
+;
+; Colin Leroy-Mira, 2024
+;
+; struct tm* __fastcall__ localtime (const time_t* timep);
+;
+
+ .export _localtime
+ .import __time_t_to_tm, __tz
+ .import ldeaxi, tosaddeax, pusheax
+ .importzp sreg
+
+_localtime:
+ cpx #$00 ; Check for null pointer
+ bne :+
+ cmp #$00
+ beq no_pointer
+: jsr ldeaxi ; Load value
+ jsr pusheax ; Push it
+ lda __tz+1+3
+ sta sreg+1
+ lda __tz+1+2
+ sta sreg
+ ldx __tz+1+1
+ lda __tz+1
+ jsr tosaddeax ; Add _tz.timezone
+ jmp __time_t_to_tm ; Convert to struct tm
+
+no_pointer:
+ rts ; A/X already set
diff --git a/libsrc/common/lz4.s b/libsrc/common/lz4.s
index 4702137bc..c53841897 100644
--- a/libsrc/common/lz4.s
+++ b/libsrc/common/lz4.s
@@ -6,6 +6,60 @@
; Almost 7 times faster, uses no RAM (vs 14 bytes BSS), and takes 1/4 the space
; vs the official C source.
;
+;
+; C implementation was:
+
+; void decompress_lz4 (unsigned char *in, unsigned char *out, const int outlen) {
+; unsigned char token, tmp;
+; unsigned int offset;
+; unsigned char *end = out+outlen;
+; unsigned char *copysrc;
+;
+; while (out < end) {
+; token = *in++;
+; offset = token >> 4;
+;
+; token &= 0x0f;
+; token += 4; // Minmatch
+;
+; if (offset == 15) {
+; moreliterals:
+; tmp = *in++;
+; offset += tmp;
+; if (tmp == 255)
+; goto moreliterals;
+; }
+;
+; if (offset) {
+; memcpy(out, in, offset);
+; out += offset;
+; in += offset;
+; }
+;
+; if (out >= end) {
+; return;
+; }
+;
+; offset = (*in);
+; in++;
+; offset += (*in)<<8;
+; in++;
+;
+; copysrc = out - offset;
+; offset = token;
+;
+; if (token == 19) {
+; morematches:
+; tmp = *in++;
+; offset += tmp;
+; if (tmp == 255)
+; goto morematches;
+; }
+;
+; memcpy(out, copysrc, offset);
+; out += offset;
+; }
+; }
.importzp sp, sreg, regsave, regbank
.importzp tmp1, tmp2, tmp3, tmp4, ptr1, ptr2, ptr3, ptr4
@@ -14,12 +68,11 @@
.export _decompress_lz4
out = regsave
-written = regsave + 2
+end = regsave + 2
tmp = tmp1
token = tmp2
offset = ptr3
in = sreg
-outlen = ptr4
; ---------------------------------------------------------------
; void decompress_lz4 (const u8 *in, u8 * const out, const u16 outlen)
@@ -29,37 +82,43 @@ outlen = ptr4
.proc _decompress_lz4: near
- sta outlen
- stx outlen+1
+ sta tmp
+ stx tmp+1
+;
+; end = out + outlen;
+;
jsr popax
sta out
- stx out+1
+ clc
+ adc tmp
+ sta end
+ txa
+ sta out+1
+ adc tmp+1
+ sta end+1
jsr popax
sta in
stx in+1
;
-; written = 0;
+; while (out < end) {
;
- lda #$00
- sta written
-;
-; while (written < outlen) {
-;
- jmp L0046
+ jmp check_len
;
; token = *in++;
;
-L0004: ldy #$00
+get_token:
+ ldy #$00
lda (in),y
- sta token
+ tay ; Backup token to Y
inc in
- bne L000A
+ bne :+
inc in+1
-L000A:
+
+:
;
; offset = token >> 4;
;
@@ -74,7 +133,7 @@ L000A:
; token &= 0xf;
; token += 4; // Minmatch
;
- lda token
+ tya ; Get token back from Y
and #$0F
clc
adc #$04
@@ -84,7 +143,8 @@ L000A:
;
lda offset
cmp #$0F
-L0013: bne L001A
+moreliterals:
+ bne check_offset_not_zero
;
; tmp = *in++;
;
@@ -93,9 +153,10 @@ L0013: bne L001A
sta tmp
inc in
- bne L0017
+ bne :+
inc in+1
-L0017:
+
+:
;
; offset += tmp;
;
@@ -113,24 +174,20 @@ L0017:
;
; goto moreliterals;
;
- jmp L0013
+ jmp moreliterals
;
; if (offset) {
;
-L001A: lda offset
+check_offset_not_zero:
+ lda offset
ora offset+1
- beq L001C
+ beq check_end
;
-; memcpy(&out[written], in, offset);
+; memcpy(out, in, offset);
;
lda out
- clc
- adc written
sta ptr2
- lda out+1
- adc written+1
- tax
- lda ptr2
+ ldx out+1
stx ptr2+1
jsr pushax
lda in
@@ -140,15 +197,15 @@ L001A: lda offset
; ldy #$00 - not needed as pushax zeroes Y
jsr memcpy_upwards
;
-; written += offset;
+; out += offset;
+; memcpy returned a pointer to out
;
- lda offset
clc
- adc written
- sta written
- lda offset+1
- adc written+1
- sta written+1
+ adc offset
+ sta out
+ txa
+ adc offset+1
+ sta out+1
;
; in += offset;
;
@@ -160,21 +217,23 @@ L001A: lda offset
adc in+1
sta in+1
;
-; if (written >= outlen)
+; if (out >= end)
;
-L001C: lda written
- cmp outlen
- lda written+1
- sbc outlen+1
+check_end:
+ lda out
+ cmp end
+ lda out+1
+ sbc end+1
;
; return;
;
- bcc L0047
+ bcc end_not_reached
rts
;
; memcpy(&offset, in, 2);
;
-L0047: ldy #$00
+end_not_reached:
+ ldy #$00
lda (in),y
sta offset
iny
@@ -187,23 +246,18 @@ L0047: ldy #$00
clc
adc in
sta in
- bcc L002F
+ bcc :+
inc in+1
+
+:
;
-; copysrc = out + written - offset;
+; copysrc = out - offset;
;
-L002F: lda out
- clc
- adc written
- tay
- lda out+1
- adc written+1
- tax
- tya
+ lda out
sec
sbc offset
sta ptr1
- txa
+ lda out+1
sbc offset+1
sta ptr1+1
;
@@ -217,7 +271,8 @@ L002F: lda out
; if (token == 19) {
;
cmp #$13
-L0045: bne L003C
+morematches:
+ bne token_not_19
;
; tmp = *in++;
;
@@ -226,9 +281,9 @@ L0045: bne L003C
sta tmp
inc in
- bne L0039
+ bne :+
inc in+1
-L0039:
+:
;
; offset += tmp;
;
@@ -246,41 +301,36 @@ L0039:
;
; goto morematches;
;
- jmp L0045
+ jmp morematches
;
-; memcpy(&out[written], copysrc, offset);
+; memcpy(out, copysrc, offset);
;
-L003C: lda out
- clc
- adc written
+token_not_19:
+ lda out
sta ptr2
- lda out+1
- adc written+1
- tax
- lda ptr2
+ ldx out+1
stx ptr2+1
jsr pushax
jsr memcpy_upwards
;
-; written += offset;
+; out += offset;
;
- lda offset
clc
- adc written
- sta written
- lda offset+1
- adc written+1
-L0046: sta written+1
+ adc offset
+ sta out
+ txa
+ adc offset+1
+ sta out+1 ; 0 on the first loop iteration
+check_len:
;
-; while (written < outlen) {
+; while (out < end) {
;
- lda written
- cmp outlen
- lda written+1
- sbc outlen+1
- jcc L0004
+ lda out
+ cmp end
+ lda out+1
+ sbc end+1
+ jcc get_token
rts
.endproc
-
diff --git a/libsrc/common/lzsa1.s b/libsrc/common/lzsa1.s
new file mode 100644
index 000000000..bbff728f7
--- /dev/null
+++ b/libsrc/common/lzsa1.s
@@ -0,0 +1,207 @@
+; void __fastcall__ decompress_lzsa1(const void *src, void *dest)
+;
+; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA1 format.
+;
+; Compress with:
+; lzsa -r -f 1 input.bin output.lzsa1
+;
+; Copyright John Brandwood 2021.
+;
+; Distributed under the Boost Software License, Version 1.0.
+; Boost Software License - Version 1.0 - August 17th, 2003
+;
+; Permission is hereby granted, free of charge, to any person or organization
+; obtaining a copy of the software and accompanying documentation covered by
+; this license (the "Software") to use, reproduce, display, distribute,
+; execute, and transmit the Software, and to prepare derivative works of the
+; Software, and to permit third-parties to whom the Software is furnished to
+; do so, all subject to the following:
+;
+; The copyright notices in the Software and this entire statement, including
+; the above license grant, this restriction and the following disclaimer,
+; must be included in all copies of the Software, in whole or in part, and
+; all derivative works of the Software, unless such copies or derivative
+; works are solely in the form of machine-executable object code generated by
+; a source language processor.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+; FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+; SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+; FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+; DEALINGS IN THE SOFTWARE.
+
+ .export _decompress_lzsa1
+
+ .import popax
+ .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
+
+lzsa_cmdbuf = tmp1 ; 1 byte.
+lzsa_winptr = ptr1 ; 1 word.
+lzsa_srcptr = ptr2 ; 1 word.
+lzsa_dstptr = ptr3 ; 1 word.
+
+lzsa_offset = lzsa_winptr
+
+.proc _decompress_lzsa1
+ sta lzsa_dstptr
+ stx lzsa_dstptr+1
+ jsr popax
+ sta lzsa_srcptr
+ stx lzsa_srcptr+1
+
+lzsa1_unpack: ldy #0 ; Initialize source index.
+ ldx #0 ; Initialize hi-byte of length.
+
+ ;
+ ; Copy bytes from compressed source data.
+ ;
+ ; N.B. X=0 is expected and guaranteed when we get here.
+ ;
+
+cp_length: lda (lzsa_srcptr),y
+ inc lzsa_srcptr
+ bne cp_skip0
+ inc lzsa_srcptr+1
+
+cp_skip0: sta lzsa_cmdbuf ; Preserve this for later.
+ and #$70 ; Extract literal length.
+ lsr ; Set CC before ...
+ beq lz_offset ; Skip directly to match?
+
+ lsr ; Get 3-bit literal length.
+ lsr
+ lsr
+ cmp #$07 ; Extended length?
+ bcc cp_got_len
+
+ jsr get_length ; X=0, CS from CMP, returns CC.
+ stx cp_npages+1 ; Hi-byte of length.
+
+cp_got_len: tax ; Lo-byte of length.
+
+cp_byte: lda (lzsa_srcptr),y ; CC throughout the execution of
+ sta (lzsa_dstptr),y ; of this .cp_page loop.
+ inc lzsa_srcptr
+ bne cp_skip1
+ inc lzsa_srcptr+1
+cp_skip1: inc lzsa_dstptr
+ bne cp_skip2
+ inc lzsa_dstptr+1
+cp_skip2: dex
+ bne cp_byte
+cp_npages: lda #0 ; Any full pages left to copy?
+ beq lz_offset
+
+ dec cp_npages+1 ; Unlikely, so can be slow.
+ bcc cp_byte ; Always true!
+
+ ;
+ ; Copy bytes from decompressed window.
+ ;
+ ; Longer but faster.
+ ;
+ ; N.B. X=0 is expected and guaranteed when we get here.
+ ;
+
+lz_offset: lda (lzsa_srcptr),y ; Get offset-lo.
+ inc lzsa_srcptr
+ bne offset_lo
+ inc lzsa_srcptr+1
+
+offset_lo: sta lzsa_offset
+
+ lda #$FF ; Get offset-hi.
+ bit lzsa_cmdbuf
+ bpl offset_hi
+
+ lda (lzsa_srcptr),y
+ inc lzsa_srcptr
+ bne offset_hi
+ inc lzsa_srcptr+1
+
+offset_hi: sta lzsa_offset+1
+
+lz_length: lda lzsa_cmdbuf ; X=0 from previous loop.
+ and #$0F
+ adc #$03 ; Always CC from .cp_page loop.
+ cmp #$12 ; Extended length?
+ bcc got_lz_len
+
+ jsr get_length ; X=0, CS from CMP, returns CC.
+
+got_lz_len: inx ; Hi-byte of length+256.
+
+ eor #$FF ; Negate the lo-byte of length
+ tay
+ eor #$FF
+
+get_lz_dst: adc lzsa_dstptr ; Calc address of partial page.
+ sta lzsa_dstptr ; Always CC from previous CMP.
+ iny
+ bcs get_lz_win
+ beq get_lz_win ; Is lo-byte of length zero?
+ dec lzsa_dstptr+1
+
+get_lz_win: clc ; Calc address of match.
+ adc lzsa_offset ; N.B. Offset is negative!
+ sta lzsa_winptr
+ lda lzsa_dstptr+1
+ adc lzsa_offset+1
+ sta lzsa_winptr+1
+
+lz_byte: lda (lzsa_winptr),y
+ sta (lzsa_dstptr),y
+ iny
+ bne lz_byte
+ inc lzsa_dstptr+1
+ dex ; Any full pages left to copy?
+ bne lz_more
+
+ jmp cp_length ; Loop around to the beginning.
+
+lz_more: inc lzsa_winptr+1 ; Unlikely, so can be slow.
+ bne lz_byte ; Always true!
+
+ ;
+ ; Get 16-bit length in X:A register pair, return with CC.
+ ;
+ ; N.B. X=0 is expected and guaranteed when we get here.
+ ;
+
+get_length: clc ; Add on the next byte to get
+ adc (lzsa_srcptr),y ; the length.
+ inc lzsa_srcptr
+ bne skip_inc
+ inc lzsa_srcptr+1
+
+skip_inc: bcc got_length ; No overflow means done.
+ clc ; MUST return CC!
+ tax ; Preserve overflow value.
+
+extra_byte: jsr get_byte ; So rare, this can be slow!
+ pha
+ txa ; Overflow to 256 or 257?
+ beq extra_word
+
+check_length: pla ; Length-lo.
+ bne got_length ; Check for zero.
+ dex ; Do one less page loop if so.
+got_length: rts
+
+extra_word: jsr get_byte ; So rare, this can be slow!
+ tax
+ bne check_length ; Length-hi == 0 at EOF.
+
+finished: pla ; Length-lo.
+ pla ; Decompression completed, pop
+ pla ; return address.
+ rts
+
+get_byte: lda (lzsa_srcptr),y ; Subroutine version for when
+ inc lzsa_srcptr ; inlining isn't advantageous.
+ bne got_byte
+ inc lzsa_srcptr+1 ; Inc & test for bank overflow.
+got_byte: rts
+.endproc
diff --git a/libsrc/common/lzsa2.s b/libsrc/common/lzsa2.s
new file mode 100644
index 000000000..e851970e1
--- /dev/null
+++ b/libsrc/common/lzsa2.s
@@ -0,0 +1,308 @@
+; void __fastcall__ decompress_lzsa2(const void *src, void *dest)
+;
+; NMOS 6502 decompressor for data stored in Emmanuel Marty's LZSA2 format.
+;
+; Compress with:
+; lzsa -r -f 2 input.bin output.lzsa2
+;
+; Copyright John Brandwood 2021.
+;
+; Distributed under the Boost Software License, Version 1.0.
+; Boost Software License - Version 1.0 - August 17th, 2003
+;
+; Permission is hereby granted, free of charge, to any person or organization
+; obtaining a copy of the software and accompanying documentation covered by
+; this license (the "Software") to use, reproduce, display, distribute,
+; execute, and transmit the Software, and to prepare derivative works of the
+; Software, and to permit third-parties to whom the Software is furnished to
+; do so, all subject to the following:
+;
+; The copyright notices in the Software and this entire statement, including
+; the above license grant, this restriction and the following disclaimer,
+; must be included in all copies of the Software, in whole or in part, and
+; all derivative works of the Software, unless such copies or derivative
+; works are solely in the form of machine-executable object code generated by
+; a source language processor.
+;
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+; FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+; SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+; FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+; ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+; DEALINGS IN THE SOFTWARE.
+
+ .export _decompress_lzsa2
+
+ .import popax
+ .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
+
+lzsa_length = lzsa_winptr ; 1 word.
+
+lzsa_cmdbuf = tmp1 ; 1 byte.
+lzsa_nibflg = tmp2 ; 1 byte.
+lzsa_nibble = tmp3 ; 1 byte.
+lzsa_offset = ptr1 ; 1 word.
+lzsa_winptr = ptr2 ; 1 word.
+lzsa_srcptr = ptr3 ; 1 word.
+lzsa_dstptr = ptr4 ; 1 word.
+
+.proc _decompress_lzsa2
+ sta lzsa_dstptr
+ stx lzsa_dstptr+1
+ jsr popax
+ sta lzsa_srcptr
+ stx lzsa_srcptr+1
+
+lzsa2_unpack:
+ ldx #$00 ; Hi-byte of length or offset.
+ ldy #$00 ; Initialize source index.
+ sty lzsa_nibflg ; Initialize nibble buffer.
+
+ ;
+ ; Copy bytes from compressed source data.
+ ;
+ ; N.B. X=0 is expected and guaranteed when we get here.
+ ;
+
+cp_length:
+ lda (lzsa_srcptr),y
+ inc lzsa_srcptr
+ bne cp_skip0
+ inc lzsa_srcptr+1
+
+cp_skip0:
+ sta lzsa_cmdbuf ; Preserve this for later.
+ and #$18 ; Extract literal length.
+ beq lz_offset ; Skip directly to match?
+
+ lsr ; Get 2-bit literal length.
+ lsr
+ lsr
+ cmp #$03 ; Extended length?
+ bcc cp_got_len
+
+ jsr get_length ; X=0 for literals, returns CC.
+ stx cp_npages+1 ; Hi-byte of length.
+
+cp_got_len:
+ tax ; Lo-byte of length.
+
+cp_byte:
+ lda (lzsa_srcptr),y ; CC throughout the execution of
+ sta (lzsa_dstptr),y ; of this .cp_page loop.
+ inc lzsa_srcptr
+ bne cp_skip1
+ inc lzsa_srcptr+1
+cp_skip1:
+ inc lzsa_dstptr
+ bne cp_skip2
+ inc lzsa_dstptr+1
+cp_skip2:
+ dex
+ bne cp_byte
+cp_npages:
+ lda #0 ; Any full pages left to copy?
+ beq lz_offset
+
+ dec cp_npages+1 ; Unlikely, so can be slow
+ bcc cp_byte ; Always true!
+
+ ;
+ ; Copy bytes from decompressed window.
+ ;
+ ; N.B. X=0 is expected and guaranteed when we get here.
+ ;
+ ; xyz
+ ; ===========================
+ ; 00z 5-bit offset
+ ; 01z 9-bit offset
+ ; 10z 13-bit offset
+ ; 110 16-bit offset
+ ; 111 repeat offset
+ ;
+
+lz_offset:
+ lda lzsa_cmdbuf
+ asl
+ bcs get_13_16_rep
+
+get_5_9_bits:
+ dex ; X=$FF for a 5-bit offset.
+ asl
+ bcs get_9_bits ; Fall through if 5-bit.
+
+get_13_bits:
+ asl ; Both 5-bit and 13-bit read
+ php ; a nibble.
+ jsr get_nibble
+ plp
+ rol ; Shift into position, clr C.
+ eor #$E1
+ cpx #$00 ; X=$FF for a 5-bit offset.
+ bne set_offset
+ sbc #2 ; 13-bit offset from $FE00.
+ bne set_hi_8 ; Always NZ from previous SBC.
+
+get_9_bits:
+ asl ; X=$FF if CC, X=$FE if CS.
+ bcc get_lo_8
+ dex
+ bcs get_lo_8 ; Always CS from previous BCC.
+
+get_13_16_rep:
+ asl
+ bcc get_13_bits ; Shares code with 5-bit path.
+
+get_16_rep:
+ bmi lz_length ; Repeat previous offset.
+
+get_16_bits:
+ jsr get_byte ; Get hi-byte of offset.
+
+set_hi_8:
+ tax
+
+get_lo_8:
+ lda (lzsa_srcptr),y ; Get lo-byte of offset.
+ inc lzsa_srcptr
+ bne set_offset
+ inc lzsa_srcptr+1
+
+set_offset:
+ sta lzsa_offset ; Save new offset.
+ stx lzsa_offset+1
+
+lz_length:
+ ldx #1 ; Hi-byte of length+256.
+
+ lda lzsa_cmdbuf
+ and #$07
+ clc
+ adc #$02
+ cmp #$09 ; Extended length?
+ bcc got_lz_len
+
+ jsr get_length ; X=1 for match, returns CC.
+ inx ; Hi-byte of length+256.
+
+got_lz_len:
+ eor #$FF ; Negate the lo-byte of length.
+ tay
+ eor #$FF
+
+get_lz_dst:
+ adc lzsa_dstptr ; Calc address of partial page.
+ sta lzsa_dstptr ; Always CC from previous CMP.
+ iny
+ bcs get_lz_win
+ beq get_lz_win ; Is lo-byte of length zero?
+ dec lzsa_dstptr+1
+
+get_lz_win:
+ clc ; Calc address of match.
+ adc lzsa_offset ; N.B. Offset is negative!
+ sta lzsa_winptr
+ lda lzsa_dstptr+1
+ adc lzsa_offset+1
+ sta lzsa_winptr+1
+
+lz_byte:
+ lda (lzsa_winptr),y
+ sta (lzsa_dstptr),y
+ iny
+ bne lz_byte
+ inc lzsa_dstptr+1
+ dex ; Any full pages left to copy?
+ bne lz_more
+
+ jmp cp_length ; Loop around to the beginning.
+
+lz_more:
+ inc lzsa_winptr+1 ; Unlikely, so can be slow.
+ bne lz_byte ; Always true!
+
+ ;
+ ; Lookup tables to differentiate literal and match lengths.
+ ;
+
+nibl_len_tbl:
+ .byte 3 ; 0+3 (for literal).
+ .byte 9 ; 2+7 (for match).
+
+byte_len_tbl:
+ .byte 18 - 1 ; 0+3+15 - CS (for literal).
+ .byte 24 - 1 ; 2+7+15 - CS (for match).
+
+ ;
+ ; Get 16-bit length in X:A register pair, return with CC.
+ ;
+
+get_length:
+ jsr get_nibble
+ cmp #$0F ; Extended length?
+ bcs byte_length
+ adc nibl_len_tbl,x ; Always CC from previous CMP.
+
+got_length:
+ ldx #$00 ; Set hi-byte of 4 & 8 bit
+ rts ; lengths.
+
+byte_length:
+ jsr get_byte ; So rare, this can be slow!
+ adc byte_len_tbl,x ; Always CS from previous CMP
+ bcc got_length
+ beq finished
+
+word_length:
+ clc ; MUST return CC!
+ jsr get_byte ; So rare, this can be slow!
+ pha
+ jsr get_byte ; So rare, this can be slow!
+ tax
+ pla
+ bne got_word ; Check for zero lo-byte.
+ dex ; Do one less page loop if so.
+got_word:
+ rts
+
+get_byte:
+ lda (lzsa_srcptr),y ; Subroutine version for when
+ inc lzsa_srcptr ; inlining isn't advantageous.
+ bne got_byte
+ inc lzsa_srcptr+1
+got_byte:
+ rts
+
+finished:
+ pla ; Decompression completed, pop
+ pla ; return address.
+ rts
+
+ ;
+ ; Get a nibble value from compressed data in A.
+ ;
+
+get_nibble:
+ lsr lzsa_nibflg ; Is there a nibble waiting?
+ lda lzsa_nibble ; Extract the lo-nibble.
+ bcs got_nibble
+
+ inc lzsa_nibflg ; Reset the flag.
+
+ lda (lzsa_srcptr),y
+ inc lzsa_srcptr
+ bne set_nibble
+ inc lzsa_srcptr+1
+
+set_nibble:
+ sta lzsa_nibble ; Preserve for next time.
+ lsr ; Extract the hi-nibble.
+ lsr
+ lsr
+ lsr
+
+got_nibble:
+ and #$0F
+ rts
+.endproc
diff --git a/libsrc/common/malloc.s b/libsrc/common/malloc.s
index 6872f1f2e..72c4aedaa 100644
--- a/libsrc/common/malloc.s
+++ b/libsrc/common/malloc.s
@@ -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
-
diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c
deleted file mode 100644
index 275589dbb..000000000
--- a/libsrc/common/mktime.c
+++ /dev/null
@@ -1,192 +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
-#include
-#include
-
-
-
-/*****************************************************************************/
-/* 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 */
-/*****************************************************************************/
-
-
-
-static unsigned char __fastcall__ IsLeapYear (unsigned Year)
-/* Returns 1 if the given year is a leap year */
-{
- return (((Year % 4) == 0) && ((Year % 100) != 0 || (Year % 400) == 0));
-}
-
-
-
-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;
- int Max;
- unsigned DayCount;
-
- /* Check if TM is valid */
- if (TM == 0) {
- /* Invalid data */
- goto Error;
- }
-
- /* Adjust seconds. */
- D = div (TM->tm_sec, 60);
- TM->tm_sec = D.rem;
-
- /* Adjust minutes */
- if (TM->tm_min + D.quot < 0) {
- goto Error;
- }
- TM->tm_min += D.quot;
- D = div (TM->tm_min, 60);
- TM->tm_min = D.rem;
-
- /* Adjust hours */
- if (TM->tm_hour + D.quot < 0) {
- goto Error;
- }
- TM->tm_hour += D.quot;
- D = div (TM->tm_hour, 24);
- TM->tm_hour = D.rem;
-
- /* Adjust days */
- if (TM->tm_mday + D.quot < 0) {
- goto Error;
- }
- TM->tm_mday += D.quot;
-
- /* 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;
- if (TM->tm_year + D.quot < 0) {
- goto Error;
- }
- 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 + 1900)) {
- Max = 29;
- } else {
- Max = MonthLength[TM->tm_mon];
- }
- if (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 + 1900)) {
- ++TM->tm_yday;
- }
-
- /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to
- ** somewhere in 2038) all years dividable by 4 are leap years, so
- ** dividing by 4 gives the days that must be added cause of leap years.
- ** (and the last leap year before 1970 was 1968)
- */
- DayCount = ((unsigned) (TM->tm_year-70)) * 365U +
- (((unsigned) (TM->tm_year-(68+1))) / 4) +
- TM->tm_yday;
-
- /* 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;
-
-Error:
- /* Error exit */
- return (time_t) -1L;
-}
-
-
-
diff --git a/libsrc/common/mktime.s b/libsrc/common/mktime.s
new file mode 100644
index 000000000..e84cee040
--- /dev/null
+++ b/libsrc/common/mktime.s
@@ -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
+ 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
+
+ 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 subtraction
+ jsr pusheax
+
+ ; Subtract 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
diff --git a/libsrc/common/pmemalign.c b/libsrc/common/pmemalign.c
index 52adb240d..4499084d1 100644
--- a/libsrc/common/pmemalign.c
+++ b/libsrc/common/pmemalign.c
@@ -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
diff --git a/libsrc/common/putenv.s b/libsrc/common/putenv.s
index 5febcc71e..13f0e7dc4 100644
--- a/libsrc/common/putenv.s
+++ b/libsrc/common/putenv.s
@@ -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
-
-
diff --git a/libsrc/common/realloc.c b/libsrc/common/realloc.c
deleted file mode 100644
index eeb1eeea5..000000000
--- a/libsrc/common/realloc.c
+++ /dev/null
@@ -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
-#include
-#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;
-}
-
-
-
diff --git a/libsrc/common/realloc.s b/libsrc/common/realloc.s
new file mode 100644
index 000000000..176871dd5
--- /dev/null
+++ b/libsrc/common/realloc.s
@@ -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 ; Subtract 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+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
diff --git a/libsrc/common/rewind.c b/libsrc/common/rewind.c
deleted file mode 100644
index 333230b74..000000000
--- a/libsrc/common/rewind.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-** rewind.c
-**
-** Christian Groessler, 07-Aug-2000
-*/
-
-
-
-#include
-
-
-
-/*****************************************************************************/
-/* Code */
-/*****************************************************************************/
-
-
-
-void __fastcall__ rewind (FILE* f)
-{
- fseek(f, 0L, SEEK_SET);
- clearerr(f);
-}
-
-
diff --git a/libsrc/common/rewind.s b/libsrc/common/rewind.s
new file mode 100644
index 000000000..e7013505c
--- /dev/null
+++ b/libsrc/common/rewind.s
@@ -0,0 +1,35 @@
+;
+; Colin Leroy-Mira
+;
+; void __fastcall__ rewind (FILE* f)
+; /* Rewind a file */
+;
+
+ .export _rewind
+
+ .import _fseek, _clearerr
+ .import pushax, pushl0, popax
+
+ .include "stdio.inc"
+
+
+; ------------------------------------------------------------------------
+; Code
+
+.proc _rewind
+
+ ; Push f twice (once for fseek, once for clearerr later)
+ jsr pushax
+ jsr pushax
+
+ ; Push offset (long) zero
+ jsr pushl0
+
+ lda #SEEK_SET
+ jsr _fseek
+
+ ; Clear error
+ jsr popax
+ jmp _clearerr
+
+.endproc
diff --git a/libsrc/common/stpcpy.s b/libsrc/common/stpcpy.s
new file mode 100644
index 000000000..c8a10db94
--- /dev/null
+++ b/libsrc/common/stpcpy.s
@@ -0,0 +1,22 @@
+;
+; Colin Leroy-Mira, 4 Sept. 2024
+;
+; char* stpcpy (char* dest, const char* src);
+;
+
+ .export _stpcpy
+ .import _strcpy
+
+ .importzp tmp1, ptr2
+
+_stpcpy:
+ jsr _strcpy
+
+ ldx ptr2+1 ; Load dest pointer's last high byte
+ tya ; Get the last offset strcpy wrote to
+
+ clc
+ adc ptr2 ; Add to low byte value
+ bcc :+
+ inx
+: rts ; Return pointer to dest's terminator
diff --git a/libsrc/common/strcasestr.s b/libsrc/common/strcasestr.s
new file mode 100644
index 000000000..58364f419
--- /dev/null
+++ b/libsrc/common/strcasestr.s
@@ -0,0 +1,95 @@
+;
+; Ullrich von Bassewitz, 11.12.1998
+;
+; char* strcasestr (const char* haystack, const char* needle);
+;
+
+ .export _strcasestr
+ .import popptr1, return0, tolowerdirect
+ .importzp ptr1, ptr2, ptr3, ptr4, tmp1, tmp2, tmp3, tmp4
+ .include "ctype.inc"
+
+ .segment "LOWCODE"
+
+_strcasestr:
+ sta ptr2 ; Save needle
+ stx ptr2+1
+ sta ptr4 ; Setup temp copy for later
+
+ jsr popptr1 ; Get haystack to ptr1
+
+; If needle is empty, return haystack
+
+ ; ldy #$00 Y=0 guaranteed by popptr1
+ lda (ptr2),y ; Get first byte of needle
+ beq @Found ; Needle is empty --> we're done
+
+; Search for the beginning of the string (this is not an optimal search
+; strategy [in fact, it's pretty dumb], but it's simple to implement).
+
+ jsr tolowerdirect ; Lowercase
+ sta tmp1 ; Save start of needle
+@L1: lda (ptr1),y ; Get next char from haystack
+ beq @NotFound ; Jump if end
+
+ jsr tolowerdirect ; Lowercase
+ cmp tmp1 ; Start of needle found?
+ beq @L2 ; Jump if so
+ iny ; Next char
+ bne @L1
+ inc ptr1+1 ; Bump high byte
+ bne @L1 ; Branch always
+
+; We found the start of needle in haystack
+
+@L2: tya ; Get offset
+ clc
+ adc ptr1
+ sta ptr1 ; Make ptr1 point to start
+ bcc @L3
+ inc ptr1+1
+
+; ptr1 points to the start of needle in haystack now. Setup temporary pointers for the
+; search. The low byte of ptr4 is already set.
+
+@L3: sta ptr3
+ lda ptr1+1
+ sta ptr3+1
+ lda ptr2+1
+ sta ptr4+1
+ ldy #1 ; First char is identical, so start on second
+
+; Do the compare
+
+@L4: lda (ptr4),y ; Get char from needle
+ beq @Found ; Jump if end of needle (-> found)
+
+ jsr tolowerdirect ; Lowercase
+ sta tmp2
+
+ lda (ptr3),y ; Compare with haystack
+
+ jsr tolowerdirect ; Lowercase
+ cmp tmp2
+ bne @L5 ; Jump if not equal
+ iny ; Next char
+ bne @L4
+ inc ptr3+1
+ inc ptr4+1 ; Bump hi byte of pointers
+ bne @L4 ; Next char (branch always)
+
+; The strings did not compare equal, search next start of needle
+
+@L5: ldy #1 ; Start after this char
+ bne @L1 ; Branch always
+
+; We found the start of needle
+
+@Found: lda ptr1
+ ldx ptr1+1
+ rts
+
+; We reached end of haystack without finding needle
+
+@NotFound:
+ jmp return0 ; return NULL
diff --git a/libsrc/common/strcpy.s b/libsrc/common/strcpy.s
index 77b39fe76..9a100f540 100644
--- a/libsrc/common/strcpy.s
+++ b/libsrc/common/strcpy.s
@@ -25,6 +25,9 @@ L1: lda (ptr1),y
inc ptr2+1
bne L1
-L9: lda ptr2 ; X still contains high byte
- rts
+L9: lda ptr2 ; X still contains dest's original high byte
+ ; On exit, we want AX to be dest (as this is what strcpy returns).
+ ; We also want (ptr2),y to still point to dest's terminator, as this
+ ; is used by stpcpy().
+ rts
diff --git a/libsrc/common/strcspn.s b/libsrc/common/strcspn.s
index 4bb01479a..418bf6ac2 100644
--- a/libsrc/common/strcspn.s
+++ b/libsrc/common/strcspn.s
@@ -7,13 +7,13 @@
.export _strcspn
.import popptr1, _strlen
- .importzp ptr1, ptr2, tmp1, tmp2
+ .importzp ptr1, ptr4, tmp1, tmp2
_strcspn:
- jsr _strlen ; get length in a/x and transfer s2 to ptr2
+ jsr _strlen ; get length in a/x and transfer s2 to ptr4
; Note: It does not make sense to
; have more than 255 test chars, so
- ; we don't support a high byte here! (ptr2+1 is
+ ; we don't support a high byte here! (ptr4+1 is
; also unchanged in strlen then (important!))
; -> the original implementation also
; ignored this case
@@ -38,7 +38,7 @@ checkNext:
iny
check: cpy tmp1 ; compare with length of test character string
beq endOfTestChars
- cmp (ptr2),y ; found matching char?
+ cmp (ptr4),y ; found matching char?
bne checkNext
leave: txa ; restore position of finding
diff --git a/libsrc/common/strdup.s b/libsrc/common/strdup.s
index 3ab07bda1..94f2cd338 100644
--- a/libsrc/common/strdup.s
+++ b/libsrc/common/strdup.s
@@ -1,85 +1,62 @@
;
; Ullrich von Bassewitz, 18.07.2000
+; Colin Leroy-Mira, 05.01.2024
;
; char* __fastcall__ strdup (const char* S);
;
-; Note: The code knowns which zero page locations are used by malloc.
+; Note: The code knowns which zero page locations are used by malloc,
+; memcpy and strlen.
;
- .importzp sp, tmp1, ptr4
- .import pushax, decsp4, incsp4
- .import _strlen, _malloc, _memcpy
+ .importzp ptr2, ptr3, ptr4, tmp1, tmp2, tmp3
+ .import _strlen_ptr4, _malloc, _memcpy, pushax
.export _strdup
.macpack cpu
- .macpack generic
_strdup:
+ ; Get length (and store source in ptr4)
+ sta ptr4
+ stx ptr4+1
+ stx tmp1 ; Backup high byte, which
+ jsr _strlen_ptr4 ; strlen may increment
-; Since we need some place to store the intermediate results, allocate a
-; stack frame. To make this somewhat more efficient, create the stackframe
-; as needed for the final call to the memcpy function.
-
- pha ; decsp will destroy A (but not X)
- jsr decsp4 ; Target/source
-
-; Store the pointer into the source slot
-
- ldy #1
- txa
- sta (sp),y
- pla
-.if (.cpu .bitand CPU_ISET_65SC02)
- sta (sp)
+ ; Add null byte for terminator
+.if (.cpu .bitand ::CPU_ISET_65SC02)
+ inc a
.else
- dey
- sta (sp),y
+ clc
+ adc #1
.endif
-
-; Get length of S (which is still in a/x)
-
- jsr _strlen
-
-; Calculate strlen(S)+1 (the space needed)
-
- add #1
- bcc @L1
+ bne :+
inx
-; Save the space we're about to allocate in ptr4
-
-@L1: sta ptr4
- stx ptr4+1
-
-; Allocate memory. _malloc will not use ptr4
+ ; Store length
+: sta tmp2
+ stx tmp3
+ ; Allocate memory
jsr _malloc
-; Store the result into the target stack slot
-
- ldy #2
- sta (sp),y ; Store low byte
- sta tmp1
- txa ; Get high byte
- iny
- sta (sp),y ; Store high byte
-
-; Check for a NULL pointer
-
- ora tmp1
+ ; Check for NULL
+ bne :+
+ cpx #$00
beq OutOfMemory
-; Copy the string. memcpy will return the target string which is exactly
-; what we need here. It will also drop the allocated stack frame.
+ ; Push dest
+: jsr pushax
+ ; Push source
lda ptr4
- ldx ptr4+1 ; Load size
- jmp _memcpy ; Copy string, drop stackframe
+ ldx tmp1
+ jsr pushax
-; Out of memory, return NULL (A = 0)
+ ; Push length
+ lda tmp2
+ ldx tmp3
+
+ ; Copy and return the dest pointer
+ jmp _memcpy
OutOfMemory:
- tax
- jmp incsp4 ; Drop stack frame
-
-
+ rts
diff --git a/libsrc/common/strftime.c b/libsrc/common/strftime.c
index 38ea4850e..2f3cadc2e 100644
--- a/libsrc/common/strftime.c
+++ b/libsrc/common/strftime.c
@@ -40,7 +40,7 @@
/* Use static local variables for speed */
-#pragma static-locals (on);
+#pragma static-locals (on)
diff --git a/libsrc/common/strlen.s b/libsrc/common/strlen.s
index 8d5bc20fc..b28bffe7a 100644
--- a/libsrc/common/strlen.s
+++ b/libsrc/common/strlen.s
@@ -2,19 +2,20 @@
; Ullrich von Bassewitz, 31.05.1998
;
; Note: strspn & strcspn call internally this function and rely on
-; the usage of only ptr2 here! Keep in mind when appling changes
+; the usage of only ptr4 here! Keep in mind when applying changes
; and check the other implementations too!
;
; size_t __fastcall__ strlen (const char* s);
;
- .export _strlen
- .importzp ptr2
+ .export _strlen, _strlen_ptr4
+ .importzp ptr4
.macpack cpu
_strlen:
- sta ptr2 ; Save s
- stx ptr2+1
+ sta ptr4 ; Save s
+ stx ptr4+1
+_strlen_ptr4:
.if (.cpu .bitand ::CPU_ISET_HUC6280)
clx
cly
@@ -27,11 +28,11 @@ _strlen:
.endif
.endif
-L1: lda (ptr2),y
+L1: lda (ptr4),y
beq L9
iny
bne L1
- inc ptr2+1
+ inc ptr4+1
inx
bne L1
diff --git a/libsrc/common/strspn.s b/libsrc/common/strspn.s
index 6fda716be..7e3f707d1 100644
--- a/libsrc/common/strspn.s
+++ b/libsrc/common/strspn.s
@@ -7,13 +7,13 @@
.export _strspn
.import popptr1, _strlen
- .importzp ptr1, ptr2, tmp1, tmp2
+ .importzp ptr1, ptr4, tmp1, tmp2
_strspn:
- jsr _strlen ; get length in a/x and transfer s2 to ptr2
+ jsr _strlen ; get length in a/x and transfer s2 to ptr4
; Note: It does not make sense to
; have more than 255 test chars, so
- ; we don't support a high byte here! (ptr2+1 is
+ ; we don't support a high byte here! (ptr4+1 is
; also unchanged in strlen then (important!))
; -> the original implementation also
; ignored this case
@@ -38,7 +38,7 @@ checkNext:
iny
check: cpy tmp1 ; compare with length of test character string
beq leave
- cmp (ptr2),y ; found matching char?
+ cmp (ptr4),y ; found matching char?
bne checkNext
foundTestChar:
diff --git a/libsrc/common/strstr.s b/libsrc/common/strstr.s
index 84f633245..691e5ba5c 100644
--- a/libsrc/common/strstr.s
+++ b/libsrc/common/strstr.s
@@ -82,14 +82,3 @@ _strstr:
lda #$00 ; return NULL
tax
rts
-
-
-
-
-
-
-
-
-
-
-
diff --git a/libsrc/common/time.s b/libsrc/common/time.s
index 40b470f5b..4092e71c6 100644
--- a/libsrc/common/time.s
+++ b/libsrc/common/time.s
@@ -6,7 +6,7 @@
.export _time
- .import decsp1, ldeaxi
+ .import pusha, ldeaxi
.importzp ptr1, sreg, tmp1, tmp2
.include "time.inc"
@@ -22,54 +22,50 @@
; Get the time (machine dependent)
- jsr decsp1
+ .assert timespec::tv_sec = 0, error
+ lda #CLOCK_REALTIME
+ jsr pusha
lda #time
jsr _clock_gettime
- sta tmp2
- lda #time
- .assert timespec::tv_sec = 0, error
- jsr ldeaxi
- sta tmp1 ; Save low byte of result
+
+; _clock_gettime returns 0 on success and -1 on error. Check that.
+
+ inx ; Did _clock_gettime return -1?
+ bne @L2 ; Jump if not
+
+; We had an error so invalidate time. A contains $FF.
+
+ ldy #3
+@L1: sta time,y
+ dey
+ bpl @L1
; Restore timep and check if it is NULL
- pla
+@L2: pla
sta ptr1+1
pla
sta ptr1 ; Restore timep
ora ptr1+1 ; timep == 0?
- beq @L1
+ beq @L4
; timep is not NULL, store the result there
ldy #3
- lda sreg+1
+@L3: lda time,y
sta (ptr1),y
dey
- lda sreg
- sta (ptr1),y
- dey
- txa
- sta (ptr1),y
- dey
- lda tmp1
- sta (ptr1),y
+ bpl @L3
-; If the result is != 0, return -1
+; Load the final result.
-@L1: lda tmp2
- beq @L2
-
- tax
- sta sreg
+@L4: lda time+3
sta sreg+1
- rts
-
-; Reload the low byte of the result and return
-
-@L2: lda tmp1
+ lda time+2
+ sta sreg
+ ldx time+1
+ lda time
rts
.endproc
diff --git a/libsrc/common/tolower.s b/libsrc/common/tolower.s
index 828be1cb1..9c143f1ce 100644
--- a/libsrc/common/tolower.s
+++ b/libsrc/common/tolower.s
@@ -10,19 +10,20 @@
; int tolower (int c);
;
- .export _tolower
+ .export _tolower, tolowerdirect
.include "ctype.inc"
.import ctypemaskdirect
_tolower:
cpx #$00 ; out of range?
- bne @L2 ; if so, return the argument unchanged
- tay ; save char
+ bne out ; if so, return the argument unchanged
+tolowerdirect:
+ pha ; save char
jsr ctypemaskdirect ; get character classification
and #CT_UPPER ; upper case char?
beq @L1 ; jump if no
- tya ; restore char
+ pla ; restore char
adc #<('a'-'A') ; make lower case char (ctypemaskdirect ensures carry clear)
rts
-@L1: tya ; restore char
-@L2: rts
+@L1: pla ; restore char
+out: rts
diff --git a/libsrc/common/ungetc.s b/libsrc/common/ungetc.s
index 7e8c1f94f..c661600af 100644
--- a/libsrc/common/ungetc.s
+++ b/libsrc/common/ungetc.s
@@ -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
-
diff --git a/libsrc/common/vsprintf.s b/libsrc/common/vsprintf.s
index b4cb9c419..65a961955 100644
--- a/libsrc/common/vsprintf.s
+++ b/libsrc/common/vsprintf.s
@@ -30,7 +30,7 @@ _vsprintf:
ldy #2
jsr staxysp
-; Contine by jumping to vsnprintf, which expects ap on the CPU stack and will
+; Continue by jumping to vsnprintf, which expects ap on the CPU stack and will
; cleanup the C stack
jmp vsnprintf
diff --git a/libsrc/common/zx02.s b/libsrc/common/zx02.s
new file mode 100644
index 000000000..02256a768
--- /dev/null
+++ b/libsrc/common/zx02.s
@@ -0,0 +1,148 @@
+; void __fastcall__ decompress_zx02(const void *src, void *dest)
+;
+; De-compressor for ZX02 files
+;
+; Compress with:
+; zx02 input.bin output.zx0
+;
+; (c) 2022 DMSC
+; Code under MIT license, see LICENSE file.
+
+ .export _decompress_zx02
+
+ .import popax
+ .importzp ptr1, ptr2, ptr3, tmp1, tmp2
+
+offset_hi = tmp1
+ZX0_src = ptr1
+ZX0_dst = ptr2
+bitr = tmp2
+pntr = ptr3
+
+.proc _decompress_zx02
+ sta ZX0_dst
+ stx ZX0_dst+1
+
+ jsr popax
+ sta ZX0_src
+ stx ZX0_src+1
+
+ ; Init values
+ lda #$80
+ sta bitr
+ ldy #$FF
+ sty pntr
+ iny
+ sty offset_hi ; Y = 0 at end of init
+
+; Decode literal: Copy next N bytes from compressed file
+; Elias(length) byte[1] byte[2] ... byte[N]
+decode_literal:
+ ldx #$01
+ jsr get_elias
+
+cop0:
+ lda (ZX0_src), y
+ inc ZX0_src
+ bne :+
+ inc ZX0_src+1
+
+: sta (ZX0_dst),y
+ inc ZX0_dst
+ bne :+
+ inc ZX0_dst+1
+
+: dex
+ bne cop0
+
+ asl bitr
+ bcs dzx0s_new_offset
+
+ ; Copy from last offset (repeat N bytes from last offset)
+ ; Elias(length)
+ inx
+ jsr get_elias
+
+dzx0s_copy:
+ lda ZX0_dst+1
+ sbc offset_hi ; C=0 from get_elias
+ sta pntr+1
+
+cop1:
+ ldy ZX0_dst
+ lda (pntr), y
+ ldy #0
+ sta (ZX0_dst),y
+ inc ZX0_dst
+ bne :+
+ inc ZX0_dst+1
+ inc pntr+1
+: dex
+ bne cop1
+
+ asl bitr
+ bcc decode_literal
+
+; Copy from new offset (repeat N bytes from new offset)
+; Elias(MSB(offset)) LSB(offset) Elias(length-1)
+dzx0s_new_offset:
+ ; Read elias code for high part of offset
+ inx
+ jsr get_elias
+ beq exit ; Read a 0, signals the end
+
+ ; Decrease and divide by 2
+ dex
+ txa
+ lsr
+ sta offset_hi
+
+ ; Get low part of offset, a literal 7 bits
+ lda (ZX0_src), y
+ inc ZX0_src
+ bne :+
+ inc ZX0_src+1
+
+: ; Divide by 2
+ ror
+ eor #$ff
+ sta pntr
+
+ ; And get the copy length.
+ ; Start elias reading with the bit already in carry:
+ ldx #1
+ jsr elias_skip1
+
+ inx
+ bcc dzx0s_copy
+
+; Read an elias-gamma interlaced code.
+elias_get:
+ ; Read next data bit to result
+ asl bitr
+ rol
+ tax
+
+get_elias:
+ ; Get one bit
+ asl bitr
+ bne elias_skip1
+
+ ; Read new bit from stream
+ lda (ZX0_src), y
+ inc ZX0_src
+ bne :+
+ inc ZX0_src+1
+
+: ; sec ; not needed, C=1 guaranteed from last bit
+ rol
+ sta bitr
+
+elias_skip1:
+ txa
+ bcs elias_get
+
+ ; Got ending bit, stop reading
+exit:
+ rts
+.endproc
diff --git a/libsrc/conio/cputs.s b/libsrc/conio/cputs.s
index 62e757b84..b822fddee 100644
--- a/libsrc/conio/cputs.s
+++ b/libsrc/conio/cputs.s
@@ -23,31 +23,17 @@ _cputsxy:
_cputs: sta ptr1 ; Save s
stx ptr1+1
+L0:
.if (.cpu .bitand CPU_ISET_65SC02)
-
-L0: lda (ptr1) ; (5)
- beq L9 ; (7) Jump if done
- jsr _cputc ; (13) Output char, advance cursor
- inc ptr1 ; (18) Bump low byte
- bne L0 ; (20) Next char
- inc ptr1+1 ; (25) Bump high byte
- bne L0
-
+ lda (ptr1) ; (5)
.else
-
-L0: ldy #0 ; (2)
-L1: lda (ptr1),y ; (7)
- beq L9 ; (9) Jump if done
- iny
- sty tmp1 ; (14) Save offset
- jsr _cputc ; (20) Output char, advance cursor
- ldy tmp1 ; (23) Get offset
- bne L1 ; (25) Next char
- inc ptr1+1 ; (30) Bump high byte
- bne L1
-
+ ldy #0 ; (2)
+ lda (ptr1),y ; (7)
.endif
-
-; Done
-
+ beq L9 ; (7/9) Jump if done
+ jsr _cputc ; (13/15) Output char, advance cursor
+ inc ptr1 ; (18/20) Bump low byte
+ bne L0 ; (20/22) Next char
+ inc ptr1+1 ; (25/27) Bump high byte
+ bne L0
L9: rts
diff --git a/libsrc/cx16/getdevice.s b/libsrc/cx16/getdevice.s
index 5f2e74af4..70f883db0 100644
--- a/libsrc/cx16/getdevice.s
+++ b/libsrc/cx16/getdevice.s
@@ -56,7 +56,7 @@ next: inx
lda STATUS
-; Either the Kernal calls above were successfull, or there was
+; Either the Kernal calls above were successful, or there was
; already a cmdchannel to the device open -- which is a pretty
; good indication of its existence. ;-)
diff --git a/libsrc/cx16/tgi/cx320p1.s b/libsrc/cx16/tgi/cx320p1.s
index 2fcd9adf3..f42d91c33 100644
--- a/libsrc/cx16/tgi/cx320p1.s
+++ b/libsrc/cx16/tgi/cx320p1.s
@@ -91,7 +91,7 @@ Y2 := ptr4
.bss
-; The colors are indicies into a TGI palette. The TGI palette is indicies into
+; The colors are indices into a TGI palette. The TGI palette is indices into
; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels.
; The first 16 RGB elements mimic the Commodore 64's colors.
@@ -267,7 +267,7 @@ GETPALETTE:
; GETDEFPALETTE: Return the default palette for the driver in .XA. All
; drivers should return something reasonable here, even drivers that don't
; support palettes, otherwise the caller has no way to determine the colors
-; of the (not changable) palette.
+; of the (not changeable) palette.
;
; Must set an error code: NO (all drivers must have a default palette)
diff --git a/libsrc/cx16/tgi/cx640p1.s b/libsrc/cx16/tgi/cx640p1.s
new file mode 100644
index 000000000..815c4ab67
--- /dev/null
+++ b/libsrc/cx16/tgi/cx640p1.s
@@ -0,0 +1,675 @@
+;
+; Graphics driver for the 640 pixels across, 480 pixels down, 2 color mode
+; on the Commander X16
+;
+; 2024-06-11, Scott Hutter
+; Based on code by Greg King
+;
+
+ .include "zeropage.inc"
+
+ .include "tgi-kernel.inc"
+ .include "tgi-error.inc"
+
+ .include "cbm_kernal.inc"
+ .include "cx16.inc"
+
+ .macpack generic
+ .macpack module
+
+
+; ------------------------------------------------------------------------
+; Header. Includes jump table and constants.
+
+ module_header _cx640p1_tgi ; 640 pixels across, 1 pixel per bit
+
+; First part of the header is a structure that has a signature,
+; and defines the capabilities of the driver.
+
+ .byte $74, $67, $69 ; ASCII "tgi"
+ .byte TGI_API_VERSION ; TGI API version number
+ .addr $0000 ; Library reference
+ .word 640 ; X resolution
+ .word 480 ; Y resolution
+ .byte 2 ; Number of drawing colors
+ .byte 0 ; Number of screens available
+ .byte 8 ; System font X size
+ .byte 8 ; System font Y size
+ .word $0100 ; Aspect ratio (based on VGA display)
+ .byte 0 ; TGI driver flags
+
+; Next, comes the jump table. Currently, all entries must be valid,
+; and may point to an RTS for test versions (function not implemented).
+
+ .addr INSTALL
+ .addr UNINSTALL
+ .addr INIT
+ .addr DONE
+ .addr GETERROR
+ .addr CONTROL
+ .addr CLEAR
+ .addr SETVIEWPAGE
+ .addr SETDRAWPAGE
+ .addr SETCOLOR
+ .addr SETPALETTE
+ .addr GETPALETTE
+ .addr GETDEFPALETTE
+ .addr SETPIXEL
+ .addr GETPIXEL
+ .addr LINE
+ .addr BAR
+ .addr TEXTSTYLE
+ .addr OUTTEXT
+
+
+; ------------------------------------------------------------------------
+; Constant
+
+
+
+; ------------------------------------------------------------------------
+; Data.
+
+; Variables mapped to the zero page segment variables. Some of these are
+; used for passing parameters to the driver.
+
+X1 = ptr1
+Y1 = ptr2
+X2 = ptr3
+Y2 = ptr4
+
+ADDR = tmp1 ; ADDR+1,2,3
+
+TEMP = tmp3
+TEMP2 = tmp4 ; HORLINE
+TEMP3 = sreg ; HORLINE
+
+
+; Absolute variables used in the code
+
+.bss
+
+; The colors are indices into a TGI palette. The TGI palette is indices into
+; VERA's palette. Vera's palette is a table of Red, Green, and Blue levels.
+; The first 16 RGB elements mimic the Commodore 64's colors.
+
+SCRBASE: .res 1 ; High byte of screen base
+BITMASK: .res 1 ; $00 = clear, $FF = set pixels
+
+defpalette: .res 2
+palette: .res 2
+
+color: .res 1 ; Stroke and fill index
+text_mode: .res 1 ; Old text mode
+
+tempX: .res 2
+tempY: .res 2
+ERR2: .res 1
+ERR: .res 1
+SY: .res 1
+SX: .res 1
+DY: .res 1
+DX: .res 1
+CURRENT_Y: .res 2
+CURRENT_X: .res 2
+
+.data
+
+ERROR: .byte TGI_ERR_OK ; Error code
+
+
+; Constants and tables
+
+.rodata
+
+veracolors:
+col_black: .byte %00000000, %00000000
+col_white: .byte %11111111, %00001111
+col_red: .byte %00000000, %00001000
+col_cyan: .byte %11111110, %00001010
+col_purple: .byte %01001100, %00001100
+col_green: .byte %11000101, %00000000
+col_blue: .byte %00001010, %00000000
+col_yellow: .byte %11100111, %00001110
+col_orange: .byte %10000101, %00001101
+col_brown: .byte %01000000, %00000110
+col_lred: .byte %01110111, %00001111
+col_gray1: .byte %00110011, %00000011
+col_gray2: .byte %01110111, %00000111
+col_lgreen: .byte %11110110, %00001010
+col_lblue: .byte %10001111, %00000000
+col_gray3: .byte %10111011, %00001011
+
+; Bit masks for setting pixels
+bitMasks1:
+ .byte %10000000, %01000000, %00100000, %00010000
+ .byte %00001000, %00000100, %00000010, %00000001
+bitMasks2:
+ .byte %01111111, %10111111, %11011111, %11101111
+ .byte %11110111, %11111011, %11111101, %11111110
+
+
+.code
+
+; ------------------------------------------------------------------------
+; INSTALL routine. Is called after the driver is loaded into memory. May
+; initialize anything that has to be done just once. Is probably empty
+; most of the time.
+;
+; Must set an error code: NO
+
+INSTALL:
+; Create the default palette.
+ lda #$00
+ sta defpalette
+ lda #$01
+ sta defpalette+1
+
+ ; Fall through.
+
+; ------------------------------------------------------------------------
+; UNINSTALL routine. Is called before the driver is removed from memory. May
+; clean up anything done by INSTALL, but is probably empty most of the time.
+;
+; Must set an error code: NO
+
+UNINSTALL:
+ rts
+
+; ------------------------------------------------------------------------
+; INIT: Changes an already installed device from text mode to graphics
+; mode.
+; Note that INIT/DONE may be called multiple times while the driver
+; is loaded, while INSTALL is called only once; so, any code that is needed
+; to initiate variables and so on must go here. Setting the palette is not
+; needed because that is called by the graphics kernel later.
+; The graphics kernel never will call INIT when a graphics mode already is
+; active, so there is no need to protect against that.
+;
+; Must set an error code: YES
+
+INIT: stz ERROR ; #TGI_ERR_OK
+
+; Save the current text mode.
+
+ sec
+ jsr SCREEN_MODE
+ sta text_mode
+
+; Switch into (640 x 480 x 2 bpp) graphics mode.
+
+ lda #%00000000 ; DCSEL = 0, VRAM port 1
+ sta VERA::CTRL
+ lda #%00100001 ; Disable sprites, layer 1 enable, VGA
+ sta VERA::DISP::VIDEO
+ lda #%00000100 ; Bitmap mode enable
+ sta VERA::L1::CONFIG
+ lda #%00000001 ; Tile width 640
+ sta VERA::L1::TILE_BASE
+ rts
+
+; ------------------------------------------------------------------------
+; DONE: Will be called to switch the graphics device back into text mode.
+; The graphics kernel never will call DONE when no graphics mode is active,
+; so there is no need to protect against that.
+;
+; Must set an error code: NO
+
+DONE:
+ jsr CINT
+ lda text_mode
+ clc
+ jmp SCREEN_MODE
+
+; ------------------------------------------------------------------------
+; GETERROR: Return the error code in .A, and clear it.
+
+GETERROR:
+ lda ERROR
+ stz ERROR
+ rts
+
+; ------------------------------------------------------------------------
+; CONTROL: Platform-/driver-specific entry point.
+;
+; Must set an error code: YES
+
+CONTROL:
+ lda #TGI_ERR_INV_FUNC
+ sta ERROR
+ rts
+
+; ------------------------------------------------------------------------
+; CLEAR: Clear the screen.
+;
+; Must set an error code: NO
+
+CLEAR:
+ .scope inner
+
+ ; set up DCSEL=2
+ lda #(2 << 1)
+ sta VERA::CTRL
+
+ ; set cache writes
+ lda #$40
+ tsb VERA::DISP::VIDEO ; VERA_FX_CTRL when DCSEL=2
+
+ ; set FX cache to all zeroes
+ lda #(6 << 1)
+ sta VERA::CTRL
+
+ lda #$00
+ sta VERA::DISP::VIDEO
+ sta VERA::DISP::HSCALE
+ sta VERA::DISP::VSCALE
+ sta VERA::DISP::FRAME
+
+ stz VERA::CTRL
+ ; set address and increment for bitmap area
+ stz VERA::ADDR
+ stz VERA::ADDR + 1
+ lda #$30 ; increment +4
+ sta VERA::ADDR + 2
+
+ ldy #$F0
+@blank_outer:
+ ldx #$0A
+@blank_loop:
+
+ .repeat 8
+ stz VERA::DATA0
+ .endrep
+
+ dex
+ bne @blank_loop
+ dey
+ bne @blank_outer
+
+ ; set up DCSEL=2
+ lda #(2 << 1)
+ sta VERA::CTRL
+
+ ; set FX off (cache write bit 1 -> 0)
+ stz VERA::DISP::VIDEO ; VERA_FX_CTRL when DCSEL=2
+ stz VERA::CTRL
+
+ .endscope
+ rts
+
+
+; ------------------------------------------------------------------------
+; SETVIEWPAGE: Set the visible page. Called with the new page in .A (0..n-1).
+; The page number already is checked to be valid by the graphics kernel.
+;
+; Must set an error code: NO (will be called only if page OK)
+
+SETVIEWPAGE:
+
+ ; Fall through.
+
+; ------------------------------------------------------------------------
+; SETDRAWPAGE: Set the drawable page. Called with the new page in .A (0..n-1).
+; The page number already is checked to be valid by the graphics kernel.
+;
+; Must set an error code: NO (will be called only if page OK)
+
+SETDRAWPAGE:
+ rts
+
+; ------------------------------------------------------------------------
+; SETPALETTE: Set the palette (not available with all drivers/hardware).
+; A pointer to the palette is passed in ptr1. Must set an error if palettes
+; are not supported
+;
+; Must set an error code: YES
+
+SETPALETTE:
+ stz ERROR ; #TGI_ERR_OK
+ ldy #$01 ; Palette size of 2 colors
+@L1: lda (ptr1),y ; Copy the palette
+ sta palette,y
+ dey
+ bpl @L1
+
+ ; set background color from palette color 0
+ lda #$00
+ sta VERA::ADDR
+ lda #$FA
+ sta VERA::ADDR+1
+ lda #$01
+ sta VERA::ADDR+2 ; write color RAM @ $1FA00
+
+ lda palette
+ asl
+ tay
+ lda veracolors,y
+ sta VERA::DATA0
+
+ inc VERA::ADDR ; $1FA01
+
+ lda palette
+ asl
+ tay
+ iny ; second byte of color
+ lda veracolors,y
+ sta VERA::DATA0
+
+ ; set foreground color from palette color 1
+ inc VERA::ADDR ; $1FA02
+
+ lda palette+1
+ asl
+ tay
+ lda veracolors,y
+ sta VERA::DATA0
+
+ inc VERA::ADDR ; $1FA03
+
+ lda palette+1
+ asl
+ tay
+ iny ; second byte of color
+ lda veracolors,y
+ sta VERA::DATA0
+ rts
+
+; ------------------------------------------------------------------------
+; SETCOLOR: Set the drawing color (in .A). The new color already is checked
+; to be in a valid range (0..maxcolor).
+;
+; Must set an error code: NO (will be called only if color OK)
+
+SETCOLOR:
+ tax
+ beq @L1
+ lda #$FF
+@L1: sta BITMASK
+ stx color
+ rts
+
+; ------------------------------------------------------------------------
+; GETPALETTE: Return the current palette in .XA. Even drivers that cannot
+; set the palette should return the default palette here, so there's no
+; way for this function to fail.
+;
+; Must set an error code: NO
+
+GETPALETTE:
+ lda #palette
+ rts
+
+; ------------------------------------------------------------------------
+; GETDEFPALETTE: Return the default palette for the driver in .XA. All
+; drivers should return something reasonable here, even drivers that don't
+; support palettes, otherwise the caller has no way to determine the colors
+; of the (not changeable) palette.
+;
+; Must set an error code: NO (all drivers must have a default palette)
+
+GETDEFPALETTE:
+ lda #defpalette
+ rts
+
+; ------------------------------------------------------------------------
+; SETPIXEL: Draw one pixel at X1/Y1 = ptr1/ptr2 with the current drawing
+; color. The co-ordinates passed to this function never are outside the
+; visible screen area, so there is no need for clipping inside this function.
+;
+; Must set an error code: NO
+
+SETPIXEL:
+ jsr CALC
+
+ stx TEMP
+
+ lda ADDR
+ ldy ADDR+1
+ ldx #$00
+
+ sta VERA::ADDR
+ sty VERA::ADDR + 1
+ stx VERA::ADDR + 2
+
+ ldx TEMP
+
+ lda BITMASK
+ beq @ahead
+
+ ; if BITMASK = $00, white is line color
+ ; Set the bit in the byte at VERA_DATA0
+ lda VERA::DATA0 ; Load the byte at memory address
+ ora bitMasks1,X ; OR with the bit mask
+ sta VERA::DATA0 ; Store back the modified byte
+ rts
+
+@ahead:
+ ; if BITMASK = $FF, black is line color
+ lda VERA::DATA0 ; Load the byte at memory address
+ and bitMasks2,X ; OR with the bit mask
+ sta VERA::DATA0 ; Store back the modified byte
+ rts
+
+; ------------------------------------------------------------------------
+; GETPIXEL: Read the color value of a pixel, and return it in .XA. The
+; co-ordinates passed to this function never are outside the visible screen
+; area, so there is no need for clipping inside this function.
+
+GETPIXEL:
+ jsr CALC
+
+ stx TEMP
+
+ lda ADDR
+ ldy ADDR+1
+ ldx #$00
+
+ sta VERA::ADDR
+ sty VERA::ADDR + 1
+ stx VERA::ADDR + 2
+
+ ldx TEMP
+ lda VERA::DATA0 ; Load the byte at memory address
+ and bitMasks1,X
+
+ bne @ahead
+
+ ldx #$00
+ lda #$00
+ rts
+
+@ahead:
+ ldx #$00
+ lda #$01
+ rts
+
+; ------------------------------------------------------------------------
+; BAR: Draw a filled rectangle with the corners X1/Y1, X2/Y2, where
+; X1/Y1 = ptr1/ptr2 and X2/Y2 = ptr3/ptr4, using the current drawing color.
+; Contrary to most other functions, the graphics kernel will sort and clip
+; the co-ordinates before calling the driver; so on entry, the following
+; conditions are valid:
+; X1 <= X2
+; Y1 <= Y2
+; (X1 >= 0) && (X1 < XRES)
+; (X2 >= 0) && (X2 < XRES)
+; (Y1 >= 0) && (Y1 < YRES)
+; (Y2 >= 0) && (Y2 < YRES)
+;
+; Must set an error code: NO
+
+BAR:
+ ; Initialize tempY with Y1
+ lda Y1
+ sta tempY
+ lda Y1+1
+ sta tempY+1
+
+@outer_loop:
+ ; Compare tempY with Y2
+ lda tempY+1
+ cmp Y2+1
+ bcc @outer_continue ; If tempY high byte < Y2 high byte, continue
+ bne @outer_end ; If tempY high byte > Y2 high byte, end
+ lda tempY
+ cmp Y2
+ bcc @outer_continue ; If tempY low byte < Y2 low byte, continue
+ beq @outer_end ; If tempY low byte = Y2 low byte, end
+
+@outer_continue:
+ ; Initialize tempX with X1
+ lda X1
+ sta tempX
+ lda X1+1
+ sta tempX+1
+
+@inner_loop:
+ ; Compare tempX with X2
+ lda tempX+1
+ cmp X2+1
+ bcc @inner_continue ; If tempX high byte < X2 high byte, continue
+ bne @inner_end ; If tempX high byte > X2 high byte, end
+ lda tempX
+ cmp X2
+ bcc @inner_continue ; If tempX low byte < X2 low byte, continue
+
+@inner_end:
+ ; Increment tempY
+ inc tempY
+ bne @outer_loop ; If no overflow, continue outer loop
+ inc tempY+1 ; If overflow, increment high byte
+
+@inner_continue:
+ ; Call setpixel(tempX, tempY)
+ lda X1
+ pha
+ lda X1+1
+ pha
+ lda Y1
+ pha
+ lda Y1+1
+ pha
+
+ lda tempX
+ ldx tempX+1
+ sta X1
+ stx X1+1
+
+ lda tempY
+ ldx tempY+1
+ sta Y1
+ stx Y1+1
+
+ jsr SETPIXEL
+
+ pla
+ sta Y1+1
+ pla
+ sta Y1
+ pla
+ sta X1+1
+ pla
+ sta X1
+
+ ; Increment tempX
+ inc tempX
+ bne @inner_loop_check ; If no overflow, continue
+ inc tempX+1 ; If overflow, increment high byte
+
+@inner_loop_check:
+ ; Compare tempX with X2 again after increment
+ lda tempX+1
+ cmp X2+1
+ bcc @inner_continue ; If tempX high byte < X2 high byte, continue
+ bne @outer_increment ; If tempX high byte > X2 high byte, increment tempY
+ lda tempX
+ cmp X2
+ bcc @inner_continue ; If tempX low byte < X2 low byte, continue
+
+@outer_increment:
+ ; Increment tempY
+ inc tempY
+ bne @outer_loop ; If no overflow, continue outer loop
+ inc tempY+1 ; If overflow, increment high byte
+
+@outer_end:
+ jmp @done
+
+@done:
+ rts
+
+; ------------------------------------------------------------------------
+; TEXTSTYLE: Set the style used when calling OUTTEXT. Text scaling in X and Y
+; directions are passed in .X and .Y, the text direction is passed in .A.
+;
+; Must set an error code: NO
+
+TEXTSTYLE:
+ rts
+
+; ------------------------------------------------------------------------
+; OUTTEXT: Output text at X/Y = ptr1/ptr2 using the current color and the
+; current text style. The text to output is given as a zero-terminated
+; string with address in ptr3.
+;
+; Must set an error code: NO
+
+OUTTEXT:
+ rts
+
+
+; ------------------------------------------------------------------------
+; Calculate all variables to plot the pixel at X1/Y1.
+;------------------------
+;< X1,Y1 - pixel
+;> ADDR - address of card
+;> X - bit number (X1 & 7)
+CALC:
+ lda Y1+1
+ sta ADDR+1
+ lda Y1
+ asl
+ rol ADDR+1
+ asl
+ rol ADDR+1 ; Y*4
+ clc
+ adc Y1
+ sta ADDR
+ lda Y1+1
+ adc ADDR+1
+ sta ADDR+1 ; Y*4+Y=Y*5
+ lda ADDR
+ asl
+ rol ADDR+1
+ asl
+ rol ADDR+1
+ asl
+ rol ADDR+1
+ asl
+ rol ADDR+1
+ sta ADDR ; Y*5*16=Y*80
+ lda X1+1
+ sta TEMP
+ lda X1
+ lsr TEMP
+ ror
+ lsr TEMP
+ ror
+ lsr TEMP
+ ror
+ clc
+ adc ADDR
+ sta ADDR
+ lda ADDR+1 ; ADDR = Y*80+x/8
+ adc TEMP
+ sta ADDR+1
+ lda ADDR+1
+ lda X1
+ and #7
+ tax
+ rts
+
+
+.include "../../tgi/tgidrv_line.inc"
diff --git a/libsrc/cx16/videomode.s b/libsrc/cx16/videomode.s
index 998316858..46f461fca 100644
--- a/libsrc/cx16/videomode.s
+++ b/libsrc/cx16/videomode.s
@@ -9,6 +9,13 @@
; #define VIDEOMODE_40x15 0x04
; #define VIDEOMODE_20x30 0x05
; #define VIDEOMODE_20x15 0x06
+; #define VIDEOMODE_22x23 0x07
+; #define VIDEOMODE_64x50 0x08
+; #define VIDEOMODE_64x25 0x09
+; #define VIDEOMODE_32x50 0x0A
+; #define VIDEOMODE_32x25 0x0B
+; #define VIDEOMODE_80COL VIDEOMODE_80x60
+; #define VIDEOMODE_40COL VIDEOMODE_40x30
; #define VIDEOMODE_320x240 0x80
; #define VIDEOMODE_SWAP (-1)
;
diff --git a/libsrc/cx16/waitvsync.s b/libsrc/cx16/waitvsync.s
index d75a7a735..e0d9f9768 100644
--- a/libsrc/cx16/waitvsync.s
+++ b/libsrc/cx16/waitvsync.s
@@ -6,7 +6,7 @@
;
; VERA's vertical sync causes IRQs which increment the jiffy timer.
;
-; Updated by ZeroByteOrg to use Kernal API RDTIM to retreive the TIMER variable
+; Updated by ZeroByteOrg to use Kernal API RDTIM to retrieve the TIMER variable
;
.export _waitvsync
diff --git a/libsrc/dbg/dbgdump.s b/libsrc/dbg/dbgdump.s
index 8ab646d21..bc6a4bd40 100644
--- a/libsrc/dbg/dbgdump.s
+++ b/libsrc/dbg/dbgdump.s
@@ -1,7 +1,7 @@
;
; Ullrich von Bassewitz, 11.08.1998
;
-; char* __cdecl__ DbgMemDump (unsigend Addr, char* Buf, unsigned char Length);
+; char* __cdecl__ DbgMemDump (unsigned Addr, char* Buf, unsigned char Length);
;
.export _DbgMemDump
diff --git a/libsrc/geos-cbm/system/get_ostype.s b/libsrc/geos-cbm/system/get_ostype.s
index 6e6731952..9ba8b7982 100644
--- a/libsrc/geos-cbm/system/get_ostype.s
+++ b/libsrc/geos-cbm/system/get_ostype.s
@@ -82,8 +82,8 @@ tvmode: ; PAL/NTSC check here, result in A
pha
lda #IO_IN ; enable access to I/O
sta CPU_DATA
- bit rasreg
- bpl tvmode ; wait for rasterline 127=256!)
modelp:
cmp rasreg ; wait for rasterline = 24 (or 280 on PAL)
diff --git a/libsrc/geos-common/const.inc b/libsrc/geos-common/const.inc
index c0294e01d..ab45a19a2 100644
--- a/libsrc/geos-common/const.inc
+++ b/libsrc/geos-common/const.inc
@@ -1,5 +1,5 @@
;
-;GeosConst - various system constans sorted by function
+;GeosConst - various system constants sorted by function
;reassembled by Maciej 'YTM/Elysium' Witkowiak
;4-2-99, 18-3-99
diff --git a/libsrc/geos-common/graph/bitotherclip.s b/libsrc/geos-common/graph/bitotherclip.s
index 020139da8..fba00d966 100644
--- a/libsrc/geos-common/graph/bitotherclip.s
+++ b/libsrc/geos-common/graph/bitotherclip.s
@@ -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)
diff --git a/libsrc/geos-common/symbols.txt b/libsrc/geos-common/symbols.txt
index d1893e376..b82954ec6 100644
--- a/libsrc/geos-common/symbols.txt
+++ b/libsrc/geos-common/symbols.txt
@@ -26,7 +26,7 @@ cardDataPntr $2c $2c $60 Word This is a pointer to the actual card graphics dat
CPU_DATA $01 n/a n/a Word Address of 6510 data register that controls the hardware memory ...
CPU_DDR $00 n/a n/a Byte Address of 6510 data direction register.
curDevice $ba $ba n/a Byte This holds the current serial device number.
-curDirHead $8200 $8200 $fa80 256 | 39 Bytes For CBM, it is the buffer containing header infomation ...
+curDirHead $8200 $8200 $fa80 256 | 39 Bytes For CBM, it is the buffer containing header information ...
curDrive $8489 $8489 $f60d Byte Holds the device number of the currently active disk drive.
curEnable n/a $1300 $0951 Byte This is an image of the C64 mobenble register.
curHeight $29 $29 $021b Byte Used to hold the card height in pixels of the current font in ...
@@ -48,7 +48,7 @@ dblClickCount $8515 $8515 $0258 Byte Used to determine when an icon is double c
devTabHi n/a n/a $fae7 4 Bytes For the Apple, these are the high and low bytes of the four ...
devTabLo n/a n/a $faeb 4 Bytes For the Apple, these are the high and low bytes of the four ...
devUnitTab n/a n/a $faef 4 Bytes The ProDos unit numbers of the four possible devices are kept ...
-dir2Head $8900 $8900 n/a 256 Bytes This is the 2nd directory header block used for larger cpacity ...
+dir2Head $8900 $8900 n/a 256 Bytes This is the 2nd directory header block used for larger capacity ...
dirBlkno n/a n/a $f620 Word Block number of the key block of the directory containing ...
dirEntryBuf $8400 $8400 $fa59 256 | 39 Bytes Buffer used to build a file's directory entry.
dirPtr n/a n/a $f622 Word Pointer into diskBlkBuf for this file's entry.
@@ -74,7 +74,7 @@ fileWritten $8498 $8498 $f61a Byte Flag indicating if a if the currently open f
firstBoot $88c5 $88c5 $0281 Byte This flag is changed from 0 to $FF when the deskTop comes up ...
fontData $850c $850c n/a(?) 9 Bytes Buffer for saving the user active font table when going into ...
fontTable $26 $26 n/a(?) 8 Bytes fontTable is a label for the beginning of variables for the ...
-grcntr12 $d016 $d016 n/a Byte Graphics control reqister #2.
+grcntr12 $d016 $d016 n/a Byte Graphics control register #2.
grcntrl1 $d011 $d011 n/a Byte Graphics control register #1.
grirq $d019 $d019 n/a Byte Graphics chip interrupt register.
grirqen $d01a $d01a n/a Byte Graphics chip interrupt enable register.
@@ -89,7 +89,7 @@ intBotVector $849f $849f $0204 Word Vector to routine to call after the operati
interleave $848c $848c n/a Byte Variable used by BlkAlloc routine as the desired interleave when ...
intSource n/a n/a $02c6 Byte Byte to indicate where interrupts are coming from on the Apple.
intTopVector $849d $849d $0202 Word Vector to routine to call before the operating system interrupt ...
-invertBuffer n/a $1ced n/a 80 Bytes Buffer area used to speed up the 80 colunn InvertLine routine.
+invertBuffer n/a $1ced n/a 80 Bytes Buffer area used to speed up the 80 column InvertLine routine.
irqvec $0314 $0314 n/a Word IRQ vector.
isGEOS $848b $848b n/a Byte Flag to indicate whether the current disk is a GEOS disk.
keyData $8504 $8504 $0245 Byte Holds the ASCII value of the current last key that was pressed.
@@ -152,7 +152,7 @@ msbxpos $d010 $d010 n/a Byte Most significant bits for x positions of sprites.
msePicPtr $31 $31 n/a Word Pointer to the mouse graphics data.
nationality $c010 $c010 $e00d Byte Byte to hold nationality of Kernal.
nmivec $0318 $0318 n/a Word NMI vector.
-noEraseSprites n/a n/a $0240 Byte Flag to stop routine TempHideMouse fron erasing sprites #2 ...
+noEraseSprites n/a n/a $0240 Byte Flag to stop routine TempHideMouse from erasing sprites #2 ...
numDrives $848d $848d $f60e Byte Set to number of drives on the system.
obj0Pointer $8ff8 $8ff8 n/a Byte Pointer to the picture data for sprite 0.
obj1Pointer $8ff9 $8ff9 n/a Byte Pointer to the picture data for sprite 1.
@@ -169,7 +169,7 @@ PrntDiskName $8476 $8476 n/a 18 Bytes Disk name that current printer driver is o
PrntFileName $8465 $8465 $08ac 17 | 16 Bytes Name of the current printer driver.
ramBase $88c7 $88c7 n/a 4 Bytes RAM bank for each disk drive to use if the drive type is either ...
ramExpSize $88c3 $88c3 n/a Byte Byte for number or RAM banks available in RAM expansion unit.
-random $850a $850a $024c Word Variable incremented each interrupt to generate a randon nunber.
+random $850a $850a $024c Word Variable incremented each interrupt to generate a randon number.
rasreg $d012 $d012 n/a Byte Raster register.
RecoverVector $84b1 $84b1 $0216 Word Pointer to routine that is called to recover the background ...
reqXpos0 n/a n/a $0800 Word This variable corresponds to the Commodore VIC chip register ...
@@ -193,7 +193,7 @@ shiftBuf n/a $1b45 $70 7 Bytes Buffer for shifting/doubling sprites. Located in
shiftOutBuf n/a $1b4c $78 7 Bytes Buffer for shifting/doubling/oring sprites. Located in back ...
sizeFlags n/a $1b53 $db1c Byte Height of sprite.
softOnes n/a $1c2d $d000 192 Bytes Buffer used for putting sprite bitmaps up on screen without ...
-softZeros n/a $1b6d $d0e0 192 Bytes Buffer used for putting sprite bitnaps up on screen without ...
+softZeros n/a $1b6d $d0e0 192 Bytes Buffer used for putting sprite bitmaps up on screen without ...
spr0pic $8a00 $8a00 n/a 64 Bytes This is where the graphics data for sprite 0 is kept on ...
spr1pic $8a40 $8a40 n/a 64 Bytes This is where the graphics data for sprite 1 is kept on ...
spr2pic $8a80 $8a80 n/a 64 Bytes This is where the graphics data for sprite 2 is kept on ...
@@ -353,8 +353,8 @@ i_Rectangle $c19f $c19f $fe3c Inline Rectangle.
ImprintLine n/a n/a $ff8f Imprint horizontal line to background buffer.
ImprintRectangle $c250 $c250 $fe4e Imprint rectangular area to background buffer.
InfoCard n/a n/a $670f Get I/O card attributes.
-InitCard n/a n/a $6700 Intialize I/O card.
-InitForDialog n/a n/a $ff4a Internal pre-dialog box intialization.
+InitCard n/a n/a $6700 Initialize I/O card.
+InitForDialog n/a n/a $ff4a Internal pre-dialog box initialization.
InitForIO $c25c $c25c n/a Prepare CBM system for I/O across serial bus.
InitForPrint $7900 $7900 $6000 Initialize printer (once per document).
InitMouse $fe80 $fe80 $f000 Initialize input device.
diff --git a/libsrc/kim1/tapeio.s b/libsrc/kim1/tapeio.s
index 4a16d6b9c..4af2b3aa2 100644
--- a/libsrc/kim1/tapeio.s
+++ b/libsrc/kim1/tapeio.s
@@ -16,7 +16,7 @@
sta ID ; Tape record ID to P1L
jsr LOADT ; Read data from tape
bcs error
- jmp return0 ; Return 0 if sucessful
+ jmp return0 ; Return 0 if successful
error: jmp return1 ; or 1 if not
.endproc
@@ -33,7 +33,7 @@ error: jmp return1 ; or 1 if not
ldx #$00
jsr DUMPT ; Write data to tape
bcs error
- jmp return0 ; Return 0 if sucessful
+ jmp return0 ; Return 0 if successful
error: jmp return1 ; or 1 if not
.endproc
diff --git a/libsrc/lynx/bootldr.s b/libsrc/lynx/bootldr.s
index ddc24faed..a0320886f 100644
--- a/libsrc/lynx/bootldr.s
+++ b/libsrc/lynx/bootldr.s
@@ -15,7 +15,7 @@
.segment "BOOTLDR"
;**********************************
; Here is the bootloader in plaintext
-; The idea is to make the smalles possible encrypted loader as decryption
+; The idea is to make the smallest possible encrypted loader as decryption
; is very slow. The minimum size is 49 bytes plus a zero byte.
;**********************************
; EXE = $fb68
diff --git a/libsrc/lynx/crt0.s b/libsrc/lynx/crt0.s
index 238a2c99d..030f523e9 100644
--- a/libsrc/lynx/crt0.s
+++ b/libsrc/lynx/crt0.s
@@ -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.
diff --git a/libsrc/lynx/eeprom66.s b/libsrc/lynx/eeprom66.s
index 6511cf8af..44b365f03 100644
--- a/libsrc/lynx/eeprom66.s
+++ b/libsrc/lynx/eeprom66.s
@@ -40,7 +40,7 @@ EE_C_WRITE = $14
EE_C_READ = $18
EE_C_ERASE = $1C
EE_C_EWEN = $13
-EE_C_EWEN2 = $FF ;; C0 schould be enough
+EE_C_EWEN2 = $FF ;; C0 should be enough
EE_C_EWDS = $10
EE_C_EWDS2 = $00
diff --git a/libsrc/lynx/eeprom86.s b/libsrc/lynx/eeprom86.s
index 73b342fae..8b829c5e8 100644
--- a/libsrc/lynx/eeprom86.s
+++ b/libsrc/lynx/eeprom86.s
@@ -41,7 +41,7 @@ EE_C_WRITE = $14
EE_C_READ = $18
EE_C_ERASE = $1C
EE_C_EWEN = $13
-EE_C_EWEN2 = $FF ;; C0 schould be enough
+EE_C_EWEN2 = $FF ;; C0 should be enough
EE_C_EWDS = $10
EE_C_EWDS2 = $00
diff --git a/libsrc/lynx/lseek.s b/libsrc/lynx/lseek.s
index 4b4f94d7c..04d816945 100644
--- a/libsrc/lynx/lseek.s
+++ b/libsrc/lynx/lseek.s
@@ -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
diff --git a/libsrc/lynx/lynx-cart.s b/libsrc/lynx/lynx-cart.s
index 94edff677..d1f3e33eb 100644
--- a/libsrc/lynx/lynx-cart.s
+++ b/libsrc/lynx/lynx-cart.s
@@ -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
diff --git a/libsrc/lynx/ser/lynx-comlynx.s b/libsrc/lynx/ser/lynx-comlynx.s
index 8aa3c838e..c4ae3d5b6 100644
--- a/libsrc/lynx/ser/lynx-comlynx.s
+++ b/libsrc/lynx/ser/lynx-comlynx.s
@@ -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
diff --git a/libsrc/lynx/tgi/lynx-160-102-16.s b/libsrc/lynx/tgi/lynx-160-102-16.s
index c35b6a5aa..00a2059aa 100644
--- a/libsrc/lynx/tgi/lynx-160-102-16.s
+++ b/libsrc/lynx/tgi/lynx-160-102-16.s
@@ -43,7 +43,7 @@ libref: .addr $0000 ; Library reference
; to an RTS for test versions (function not implemented). A future version may
; allow for emulation: In this case the vector will be zero. Emulation means
; that the graphics kernel will emulate the function by using lower level
-; primitives - for example ploting a line by using calls to SETPIXEL.
+; primitives - for example plotting a line by using calls to SETPIXEL.
.addr INSTALL
.addr UNINSTALL
@@ -258,7 +258,7 @@ GETERROR:
;
; The TGI lacks a way to draw sprites. As that functionality is vital to
; Lynx games we borrow this CONTROL function to implement the still
-; missing tgi_draw_sprite funtion. To use this in your C-program
+; missing tgi_draw_sprite function. To use this in your C-program
; do a #define tgi_draw_sprite(spr) tgi_ioctl(0, spr)
;
; To do a flip-screen call tgi_ioctl(1, 0)
diff --git a/libsrc/lynx/uploader.s b/libsrc/lynx/uploader.s
index f16a1721a..5ce21b489 100644
--- a/libsrc/lynx/uploader.s
+++ b/libsrc/lynx/uploader.s
@@ -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
diff --git a/libsrc/nes/color.s b/libsrc/nes/color.s
index e3aef9a28..cd1a950ea 100644
--- a/libsrc/nes/color.s
+++ b/libsrc/nes/color.s
@@ -9,11 +9,11 @@
.export _textcolor, _bgcolor, _bordercolor
- .import return0, ppubuf_put
+ .import return0, return1, ppubuf_put
.include "nes.inc"
-_textcolor = return0
+_textcolor = return1
_bordercolor = return0
.proc _bgcolor
diff --git a/libsrc/nes/cpeekc.s b/libsrc/nes/cpeekc.s
new file mode 100644
index 000000000..315a2df5c
--- /dev/null
+++ b/libsrc/nes/cpeekc.s
@@ -0,0 +1,37 @@
+;
+; 2016-02-28, Groepaz
+; 2017-08-17, Greg King
+;
+; char cpeekc (void);
+;
+
+ .export _cpeekc
+
+ .import ppubuf_waitempty
+ .forceimport initconio
+
+ .include "nes.inc"
+
+
+_cpeekc:
+ ; wait until all console data has been written
+ jsr ppubuf_waitempty
+
+ ldy SCREEN_PTR+1
+ lda SCREEN_PTR
+
+; waiting for vblank is incredibly slow ://
+vwait:
+; ldx PPU_STATUS
+; bpl vwait
+
+ ldx #>$0000
+ sty PPU_VRAM_ADDR2
+ sta PPU_VRAM_ADDR2
+ lda PPU_VRAM_IO ; first read is invalid
+ lda PPU_VRAM_IO ; get data
+ stx PPU_VRAM_ADDR2
+ stx PPU_VRAM_ADDR2
+
+ and #<~$80 ; remove reverse bit
+ rts
diff --git a/libsrc/nes/cpeekcolor.s b/libsrc/nes/cpeekcolor.s
new file mode 100644
index 000000000..ed275ec95
--- /dev/null
+++ b/libsrc/nes/cpeekcolor.s
@@ -0,0 +1,8 @@
+;
+; 2017-06-03, Greg King
+;
+; unsigned char cpeekcolor (void);
+;
+
+ .import return1
+ .export _cpeekcolor := return1 ; always COLOR_WHITE
diff --git a/libsrc/nes/cpeekrevers.s b/libsrc/nes/cpeekrevers.s
new file mode 100644
index 000000000..8b1542bc0
--- /dev/null
+++ b/libsrc/nes/cpeekrevers.s
@@ -0,0 +1,37 @@
+;
+; 2016-02-28, Groepaz
+; 2017-08-17, Greg King
+;
+; char cpeekrevers (void);
+;
+
+ .export _cpeekrevers
+
+ .import ppubuf_waitempty
+ .forceimport initconio
+
+ .include "nes.inc"
+
+
+_cpeekrevers:
+ ; wait until all console data has been written
+ jsr ppubuf_waitempty
+
+ ldy SCREEN_PTR+1
+ lda SCREEN_PTR
+
+; waiting for vblank is incredibly slow ://
+vwait:
+; ldx PPU_STATUS
+; bpl vwait
+
+ ldx #>$0000
+ sty PPU_VRAM_ADDR2
+ sta PPU_VRAM_ADDR2
+ lda PPU_VRAM_IO ; first read is invalid
+ lda PPU_VRAM_IO ; get data
+ stx PPU_VRAM_ADDR2
+ stx PPU_VRAM_ADDR2
+
+ and #<$80 ; get reverse bit
+ rts
diff --git a/libsrc/nes/ppubuf.s b/libsrc/nes/ppubuf.s
index f08fc1a71..0de6d1980 100644
--- a/libsrc/nes/ppubuf.s
+++ b/libsrc/nes/ppubuf.s
@@ -68,7 +68,7 @@
; ------------------------------------------------------------------------
; Flush PPU-Memory write buffer
-; called from vblank interupt
+; called from vblank interrupt
.proc ppubuf_flush
diff --git a/libsrc/osic1p/cpeekc.s b/libsrc/osic1p/cpeekc.s
new file mode 100644
index 000000000..55a170501
--- /dev/null
+++ b/libsrc/osic1p/cpeekc.s
@@ -0,0 +1,17 @@
+;
+; 2017-06-21, Greg King
+;
+; char cpeekc (void);
+;
+; Get a character from OSI C1P screen RAM.
+;
+ .export _cpeekc
+
+ .include "extzp.inc"
+
+
+_cpeekc:
+ ldy CURS_X
+ lda (SCREEN_PTR),y
+ ldx #>$0000
+ rts
diff --git a/libsrc/osic1p/cpeekcolor.s b/libsrc/osic1p/cpeekcolor.s
new file mode 100644
index 000000000..ed275ec95
--- /dev/null
+++ b/libsrc/osic1p/cpeekcolor.s
@@ -0,0 +1,8 @@
+;
+; 2017-06-03, Greg King
+;
+; unsigned char cpeekcolor (void);
+;
+
+ .import return1
+ .export _cpeekcolor := return1 ; always COLOR_WHITE
diff --git a/libsrc/osic1p/cpeekrevers.s b/libsrc/osic1p/cpeekrevers.s
new file mode 100644
index 000000000..ab7c68b35
--- /dev/null
+++ b/libsrc/osic1p/cpeekrevers.s
@@ -0,0 +1,9 @@
+;
+; 2017-06-15, Greg King
+;
+; unsigned char cpeekrevers (void);
+;
+; Get a reverse attribute from screen RAM
+;
+ .import return0
+ .export _cpeekrevers := return0 ; No attribute
diff --git a/libsrc/pce/cpeekc.s b/libsrc/pce/cpeekc.s
index e144f94ac..326fe5203 100644
--- a/libsrc/pce/cpeekc.s
+++ b/libsrc/pce/cpeekc.s
@@ -14,11 +14,11 @@ _cpeekc:
st0 #VDC_MARR ; Memory-Address Read
ldy SCREEN_PTR
ldx SCREEN_PTR+1
- sty VDC_DATA_LO
- stx VDC_DATA_HI
+ sty a:VDC_DATA_LO
+ stx a:VDC_DATA_HI
st0 #VDC_VRR ; VRAM Read Register
- lda VDC_DATA_LO ; character
+ lda a:VDC_DATA_LO ; character
and #<~$80 ; remove reverse bit
ldx #0
rts
diff --git a/libsrc/pce/cpeekcolor.s b/libsrc/pce/cpeekcolor.s
index 8b96d29d4..ecee91ed5 100644
--- a/libsrc/pce/cpeekcolor.s
+++ b/libsrc/pce/cpeekcolor.s
@@ -14,11 +14,11 @@ _cpeekcolor:
st0 #VDC_MARR ; Memory-Address Read
ldy SCREEN_PTR
ldx SCREEN_PTR+1
- sty VDC_DATA_LO
- stx VDC_DATA_HI
+ sty a:VDC_DATA_LO
+ stx a:VDC_DATA_HI
st0 #VDC_VRR ; VRAM Read Register
- lda VDC_DATA_HI
+ lda a:VDC_DATA_HI
and #<~$02
lsr a
lsr a
diff --git a/libsrc/pce/cpeekrevers.s b/libsrc/pce/cpeekrevers.s
index 3f208fd10..8fa173202 100644
--- a/libsrc/pce/cpeekrevers.s
+++ b/libsrc/pce/cpeekrevers.s
@@ -14,13 +14,14 @@ _cpeekrevers:
st0 #VDC_MARR ; Memory-Address Read
ldy SCREEN_PTR
ldx SCREEN_PTR+1
- sty VDC_DATA_LO
- stx VDC_DATA_HI
+ sty a:VDC_DATA_LO
+ stx a:VDC_DATA_HI
st0 #VDC_VRR ; VRAM Read Register
- lda VDC_DATA_LO ; character (bit 7 is revers bit)
- rol a
- rol a
- and #1
- ldx #0
+
+ lda a:VDC_DATA_LO ; character (bit 7 is revers bit)
+ and #$80 ; get reverse bit
+ asl a ; reverse bit to carry, A=0
+ tax ; ldx #>$0000
+ rol a ; reverse bit from carry
rts
diff --git a/libsrc/pce/cpeeks.s b/libsrc/pce/cpeeks.s
index fe5e28687..322b9ba30 100644
--- a/libsrc/pce/cpeeks.s
+++ b/libsrc/pce/cpeeks.s
@@ -1,5 +1,6 @@
;
; 2020-07-14, Groepaz
+; 2020-07-15, Greg King
;
; void cpeeks (char* s, unsigned length);
;
@@ -8,9 +9,7 @@
.export _cpeeks
.import popax
- .importzp ptr1, ptr2, tmp1, tmp2
-
- .macpack generic
+ .importzp ptr1, ptr2
.include "pce.inc"
.include "extzp.inc"
@@ -22,35 +21,31 @@ _cpeeks:
eor #>$FFFF
sta ptr2+1
+ st0 #VDC_CR ; Control Register
+ st2 #>$0088 ; make VRAM address increment by one
+
st0 #VDC_MARR ; Memory-Address Read
ldy SCREEN_PTR
ldx SCREEN_PTR+1
- sty VDC_DATA_LO
- stx VDC_DATA_HI
+ sty a:VDC_DATA_LO
+ stx a:VDC_DATA_HI
st0 #VDC_VRR ; VRAM Read Register
jsr popax
- sta tmp1 ; (will be a .Y index)
+ tay ; low byte of address will be used as index
stx ptr1+1
-
ldx #<$0000
stx ptr1
beq L2 ; branch always
-L3: ldy tmp2
- lda VDC_DATA_LO ; get character
- bit VDC_DATA_HI ; we need to "read" the highbyte to advance the address
- iny
- sty tmp2
+L3: lda a:VDC_DATA_LO ; get character
+ bit a:VDC_DATA_HI ; need to read high byte to advance VDC address
and #<~$80 ; remove reverse bit
-
- ldy tmp1
sta (ptr1),y
iny
- bne L1
+ bne L2
inc ptr1+1
-L1: sty tmp1
L2: inc ptr2 ; count length
bne L3
@@ -58,6 +53,5 @@ L2: inc ptr2 ; count length
bne L3
txa ; terminate the string
- ldy tmp1
sta (ptr1),y
rts
diff --git a/libsrc/pce/vga.s b/libsrc/pce/vga.s
index 630fbe8db..067647cb8 100644
--- a/libsrc/pce/vga.s
+++ b/libsrc/pce/vga.s
@@ -3,9 +3,9 @@
.export _pce_font
-; The character tiles use only two colors from each pallette. Color zero
-; comes from pallette zero; color one is different in each pallette. The
-; color of a character is set by choosing one of the 16 pallettes.
+; The character tiles use only two colors from each palette. Color zero
+; comes from palette zero; color one is different in each palette. The
+; color of a character is set by choosing one of the 16 palettes.
.rodata
diff --git a/libsrc/pet/cbm_load.c b/libsrc/pet/cbm_load.c
index 1823f6537..2c98d9581 100644
--- a/libsrc/pet/cbm_load.c
+++ b/libsrc/pet/cbm_load.c
@@ -22,14 +22,14 @@ unsigned int __fastcall__ cbm_load (const char* name, unsigned char device, void
unsigned int size = 0;
if (cbm_open (1, device, CBM_READ, name) != 0) {
- /* Can't load from a file that can't be openned. */
+ /* Can't load from a file that can't be opened. */
return 0;
}
/* Get the file's load address. */
if (cbm_read (1, &load, sizeof load) != sizeof load) {
/* Either the file wasn't found, or it was too short. (Note:
- ** the computer openned a file even if the drive couldn't open one.)
+ ** the computer opened a file even if the drive couldn't open one.)
*/
cbm_close (1);
return 0;
diff --git a/libsrc/pet/waitvsync.s b/libsrc/pet/waitvsync.s
index 39b562e43..d74f76c9f 100644
--- a/libsrc/pet/waitvsync.s
+++ b/libsrc/pet/waitvsync.s
@@ -9,8 +9,7 @@
.include "pet.inc"
_waitvsync:
-@l1:
- lda VIA_PB
- and #%00100000
- bne @l1
+ lda #%00100000
+: and VIA_PB
+ bne :-
rts
diff --git a/libsrc/plus4/kgetin.s b/libsrc/plus4/kgetin.s
new file mode 100644
index 000000000..e77395b9a
--- /dev/null
+++ b/libsrc/plus4/kgetin.s
@@ -0,0 +1,17 @@
+
+ .export GETIN
+
+.scope KERNAL
+ .include "cbm_kernal.inc"
+.endscope
+
+ .include "plus4.inc"
+
+.segment "LOWCODE" ; Stay out of ROM area.
+
+.proc GETIN
+ sta ENABLE_ROM
+ jsr KERNAL::GETIN
+ sta ENABLE_RAM
+ rts
+.endproc
diff --git a/libsrc/rp6502/clock.c b/libsrc/rp6502/clock.c
new file mode 100644
index 000000000..f8756f553
--- /dev/null
+++ b/libsrc/rp6502/clock.c
@@ -0,0 +1,7 @@
+#include
+#include
+
+clock_t __fastcall__ clock (void)
+{
+ return ria_call_long (RIA_OP_CLOCK);
+}
diff --git a/libsrc/rp6502/read.c b/libsrc/rp6502/read.c
index eb96f779c..87a9bbf7d 100644
--- a/libsrc/rp6502/read.c
+++ b/libsrc/rp6502/read.c
@@ -5,7 +5,7 @@ int __fastcall__ read (int fildes, void* buf, unsigned count)
{
int total = 0;
while (count) {
- unsigned blockcount = (count > 256) ? 256 : count;
+ unsigned blockcount = (count > 512) ? 512 : count;
int bytes_read = read_xstack (&((char*)buf)[total], blockcount, fildes);
if (bytes_read < 0) {
return bytes_read;
diff --git a/libsrc/rp6502/sysrename.c b/libsrc/rp6502/sysrename.c
index 96eca24cf..46bdd8b31 100644
--- a/libsrc/rp6502/sysrename.c
+++ b/libsrc/rp6502/sysrename.c
@@ -7,7 +7,7 @@ unsigned char __fastcall__ _sysrename (const char* oldpath, const char* newpath)
size_t oldpathlen, newpathlen;
oldpathlen = strlen (oldpath);
newpathlen = strlen (newpath);
- if (oldpathlen + newpathlen > 254) {
+ if (oldpathlen + newpathlen > 510) {
return _mappederrno (EINVAL);
}
while (oldpathlen) {
diff --git a/libsrc/rp6502/write.c b/libsrc/rp6502/write.c
index 11241dab5..23877b6e8 100644
--- a/libsrc/rp6502/write.c
+++ b/libsrc/rp6502/write.c
@@ -5,7 +5,7 @@ int __fastcall__ write (int fildes, const void* buf, unsigned count)
{
int ax, total = 0;
while (count) {
- int blockcount = (count > 256) ? 256 : count;
+ int blockcount = (count > 512) ? 512 : count;
ax = write_xstack (&((char*)buf)[total], blockcount, fildes);
if (ax < 0) {
return ax;
diff --git a/libsrc/rp6502/write_xstack.c b/libsrc/rp6502/write_xstack.c
index b53aa95e7..29285a87e 100644
--- a/libsrc/rp6502/write_xstack.c
+++ b/libsrc/rp6502/write_xstack.c
@@ -1,8 +1,12 @@
#include
+#include
int __fastcall__ write_xstack (const void* buf, unsigned count, int fildes)
{
unsigned i;
+ if (count > 512) {
+ return _mappederrno (EINVAL);
+ }
for (i = count; i;) {
ria_push_char (((char*)buf)[--i]);
}
diff --git a/libsrc/rp6502/xregn.c b/libsrc/rp6502/xregn.c
new file mode 100644
index 000000000..ec040be20
--- /dev/null
+++ b/libsrc/rp6502/xregn.c
@@ -0,0 +1,19 @@
+#include
+#include
+
+int __cdecl__ xregn (char device, char channel, unsigned char address, unsigned count,
+ ...)
+{
+ va_list args;
+ va_start (args, count);
+ RIA.xstack = device;
+ RIA.xstack = channel;
+ RIA.xstack = address;
+ while (count--) {
+ unsigned v = va_arg (args, unsigned);
+ RIA.xstack = v >> 8;
+ RIA.xstack = v;
+ }
+ va_end (args);
+ return ria_call_int_errno (RIA_OP_XREG);
+}
diff --git a/libsrc/runtime/aslax7.s b/libsrc/runtime/aslax7.s
new file mode 100644
index 000000000..533ee55e6
--- /dev/null
+++ b/libsrc/runtime/aslax7.s
@@ -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
diff --git a/libsrc/runtime/asrax7.s b/libsrc/runtime/asrax7.s
new file mode 100644
index 000000000..3c9cce681
--- /dev/null
+++ b/libsrc/runtime/asrax7.s
@@ -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
diff --git a/libsrc/runtime/callirq.s b/libsrc/runtime/callirq.s
index 74a12c4db..2096e5c3b 100644
--- a/libsrc/runtime/callirq.s
+++ b/libsrc/runtime/callirq.s
@@ -12,7 +12,7 @@
;
; 2. Reentrancy. The condes routines must use self modyfiying code, which
; means it is not reentrant. An IRQ using condes, that interrupts
-; another use of condes will cause unpredicatble behaviour. The current
+; another use of condes will cause unpredictable behaviour. The current
; code avoids this by using locking mechanisms, but it's complex and
; has a size and performance penalty.
;
diff --git a/libsrc/runtime/lshelp.s b/libsrc/runtime/lshelp.s
index 8a2fca139..ce1648d28 100644
--- a/libsrc/runtime/lshelp.s
+++ b/libsrc/runtime/lshelp.s
@@ -39,7 +39,7 @@ poplsargs:
adc #$00
sta sreg+1
-L1: lda ptr4+1 ; Is the right operand nagative?
+L1: lda ptr4+1 ; Is the right operand negative?
sta tmp2 ; Remember the sign for later
bpl L2 ; Jump if not
diff --git a/libsrc/runtime/ludiv.s b/libsrc/runtime/ludiv.s
index e2e27371e..77335d8f5 100644
--- a/libsrc/runtime/ludiv.s
+++ b/libsrc/runtime/ludiv.s
@@ -22,7 +22,7 @@ tosudiv0ax:
.endif
tosudiveax:
- jsr getlop ; Get the paramameters
+ jsr getlop ; Get the parameters
jsr udiv32 ; Do the division
lda ptr1 ; Result is in ptr1:sreg
ldx ptr1+1
diff --git a/libsrc/runtime/lumod.s b/libsrc/runtime/lumod.s
index 09909c0c9..eb6176b35 100644
--- a/libsrc/runtime/lumod.s
+++ b/libsrc/runtime/lumod.s
@@ -22,7 +22,7 @@ tosumod0ax:
.endif
tosumodeax:
- jsr getlop ; Get the paramameters
+ jsr getlop ; Get the parameters
jsr udiv32 ; Do the division
lda tmp3 ; Remainder is in ptr2:tmp3:tmp4
sta sreg
diff --git a/libsrc/runtime/popptr1.s b/libsrc/runtime/popptr1.s
index 1d04330ab..b54bb9eb3 100644
--- a/libsrc/runtime/popptr1.s
+++ b/libsrc/runtime/popptr1.s
@@ -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
diff --git a/libsrc/runtime/pushax.s b/libsrc/runtime/pushax.s
index ac181b994..27ddf641d 100644
--- a/libsrc/runtime/pushax.s
+++ b/libsrc/runtime/pushax.s
@@ -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
diff --git a/libsrc/runtime/pushptr1.s b/libsrc/runtime/pushptr1.s
new file mode 100644
index 000000000..ca934d6c1
--- /dev/null
+++ b/libsrc/runtime/pushptr1.s
@@ -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
diff --git a/libsrc/runtime/returnFFFF.s b/libsrc/runtime/returnFFFF.s
new file mode 100644
index 000000000..41180651b
--- /dev/null
+++ b/libsrc/runtime/returnFFFF.s
@@ -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
diff --git a/libsrc/runtime/shrax7.s b/libsrc/runtime/shrax7.s
new file mode 100644
index 000000000..31712ca72
--- /dev/null
+++ b/libsrc/runtime/shrax7.s
@@ -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
diff --git a/libsrc/sim6502/exehdr.s b/libsrc/sim6502/exehdr.s
index 09d099da5..6487e5b0c 100644
--- a/libsrc/sim6502/exehdr.s
+++ b/libsrc/sim6502/exehdr.s
@@ -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 Unknown CPU type.
+.endif
.byte sp ; sp address
.addr __MAIN_START__ ; load address
.addr startup ; reset address
diff --git a/libsrc/sim6502/paravirt.s b/libsrc/sim6502/paravirt.s
index 0d8e528b1..b9fc38de5 100644
--- a/libsrc/sim6502/paravirt.s
+++ b/libsrc/sim6502/paravirt.s
@@ -7,11 +7,17 @@
; int __fastcall__ write (int fd, const void* buf, unsigned count);
;
- .export exit, args, _open, _close, _read, _write
+ .export exit, args, _open, _close, _read, _write, _lseek
+ .export __sysremove, ___osmaperrno
+_lseek := $FFF1
+__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!
diff --git a/libsrc/sym1/tapeio.s b/libsrc/sym1/tapeio.s
index 078ea7abd..f94e1336c 100644
--- a/libsrc/sym1/tapeio.s
+++ b/libsrc/sym1/tapeio.s
@@ -21,7 +21,7 @@
ldy #$80
jsr LOADT ; Read data from tape
bcs error
- jmp return0 ; Return 0 if sucessful
+ jmp return0 ; Return 0 if successful
error: jmp return1 ; or 1 if not
.endproc
@@ -40,7 +40,7 @@ error: jmp return1 ; or 1 if not
ldy #$80
jsr DUMPT ; Write data to tape
bcs error
- jmp return0 ; Return 0 if sucessful
+ jmp return0 ; Return 0 if successful
error: jmp return1 ; or 1 if not
.endproc
diff --git a/libsrc/telestrat/write.s b/libsrc/telestrat/write.s
index 37a896696..8509aa159 100644
--- a/libsrc/telestrat/write.s
+++ b/libsrc/telestrat/write.s
@@ -65,7 +65,7 @@ L2: ldy #0
cpx #$0A ; check for \n
bne L3
BRK_TELEMON XWR0 ; macro send char to screen (channel 0 in telemon terms)
- lda #$0D ; return to the beggining of the line
+ lda #$0D ; return to the beginning of the line
BRK_TELEMON XWR0 ; macro
diff --git a/libsrc/tgi/tgi_clippedline.s b/libsrc/tgi/tgi_clippedline.s
index b0f1dd456..319bb7152 100644
--- a/libsrc/tgi/tgi_clippedline.s
+++ b/libsrc/tgi/tgi_clippedline.s
@@ -188,7 +188,7 @@ tgi_clip_sign: .res 1
ldx tgi_clip_dx+1
jsr udiv32by16r16
-; Check the sign of the final result and negate it if nessary
+; Check the sign of the final result and negate it if necessary
done: bit tmp1
jmi negax
@@ -228,7 +228,7 @@ done: bit tmp1
ldx tgi_clip_dy+1
jsr udiv32by16r16
-; Check the sign of the final result and negate it if nessary
+; Check the sign of the final result and negate it if necessary
jmp muldiv_dydx::done
@@ -279,7 +279,7 @@ L1: lda tgi_clip_o1
; We must clip. If we haven't already done so, calculate dx/dy.
-L2: lda tgi_clip_d ; Deltas alreay calculated?
+L2: lda tgi_clip_d ; Deltas already calculated?
bne HaveDeltas ; Jump if yes
inc tgi_clip_d
jsr calcdeltas
diff --git a/libsrc/tgi/tgi_init.s b/libsrc/tgi/tgi_init.s
index c8fd355d5..79651cbba 100644
--- a/libsrc/tgi/tgi_init.s
+++ b/libsrc/tgi/tgi_init.s
@@ -63,7 +63,7 @@
lda #<$100
ldx #>$100
jsr pushax ; Width scale = 1.0
- jsr pushax ; Heigh scale = 1.0
+ jsr pushax ; Height scale = 1.0
jsr pusha ; Text direction = TGI_TEXT_HORIZONTAL
jmp _tgi_settextstyle ; A = Font = TGI_FONT_BITMAP
diff --git a/libsrc/vic20/joy/vic20-stdjoy.s b/libsrc/vic20/joy/vic20-stdjoy.s
index b3de8766c..22ec0b638 100644
--- a/libsrc/vic20/joy/vic20-stdjoy.s
+++ b/libsrc/vic20/joy/vic20-stdjoy.s
@@ -82,7 +82,7 @@ COUNT:
; ------------------------------------------------------------------------
; READ: Read a particular joystick passed in A.
-; The current implemenation will ignore the joystick number because we do only
+; The current implementation will ignore the joystick number because we do only
; have one joystick
READ: lda #$7F ; mask for VIA2 JOYBIT: sw3
diff --git a/libsrc/zlib/crc32.s b/libsrc/zlib/crc32.s
index 41b5fe9db..f80b1f437 100644
--- a/libsrc/zlib/crc32.s
+++ b/libsrc/zlib/crc32.s
@@ -1,6 +1,7 @@
;
; 2001-11-14, Piotr Fusik
; 2018-05-20, Christian Kruger
+; 2025-05-14, Piotr Fusik
;
; unsigned long __fastcall__ crc32 (unsigned long crc,
; const unsigned char* buf,
@@ -9,7 +10,7 @@
.export _crc32
- .import compleax, incsp2, incsp4, popptr1, popeax
+ .import compleax, incsp4, popptr1, popeax
.importzp sreg, ptr1, ptr2, tmp1, tmp2
POLYNOMIAL = $EDB88320
@@ -58,15 +59,12 @@ make_table:
inx
bne @L1
inc table_initialised
-RET:
rts
_crc32:
-; ptr2 = (len & 0xff) == 0 ? len : len + 0x100;
- tay
- beq @L1
+; ptr2 = len + 0x100
inx
-@L1: sta ptr2
+ sta ptr2
stx ptr2+1
; ptr1 = buf
jsr popptr1
@@ -78,20 +76,15 @@ _crc32:
bne @dont_make
jsr make_table
@dont_make:
-; eax = crc
- jsr popeax
-; if (len == 0) return crc;
- ldy ptr2
- bne @L2
- ldy ptr2+1
- beq RET
-@L2:
; eax = ~crc
+ jsr popeax
jsr compleax
stx tmp2
ldy #0
+@L1: cpy ptr2
+ beq @low_end
; crc = (crc >> 8) ^ table[(crc & 0xff) ^ *p++];
-@L3: eor (ptr1),y
+@L2: eor (ptr1),y
tax
lda table_0,x
eor tmp2
@@ -106,12 +99,12 @@ _crc32:
sta sreg+1
lda tmp1
iny
- bne @L4
+ bne @L1
inc ptr1+1
-@L4: dec ptr2
- bne @L3
+ jmp @L1
+@low_end:
dec ptr2+1
- bne @L3
+ bne @L2
ldx tmp2
jmp compleax
diff --git a/samples/Makefile b/samples/Makefile
index 0e5292306..3680f542c 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -562,6 +562,11 @@ install:
$(INSTALL) -d $(DESTDIR)$(samplesdir)/cbm
$(INSTALL) -d $(DESTDIR)$(samplesdir)/gamate
$(INSTALL) -d $(DESTDIR)$(samplesdir)/supervision
+ $(INSTALL) -d $(DESTDIR)$(samplesdir)/disasm
+ $(INSTALL) -d $(DESTDIR)$(samplesdir)/kim1
+ $(INSTALL) -d $(DESTDIR)$(samplesdir)/lynx
+ $(INSTALL) -d $(DESTDIR)$(samplesdir)/sim65
+ $(INSTALL) -d $(DESTDIR)$(samplesdir)/sym1
$(INSTALL) -m0644 *.* $(DESTDIR)$(samplesdir)
$(INSTALL) -m0644 readme.txt $(DESTDIR)$(samplesdir)
$(INSTALL) -m0644 Makefile $(DESTDIR)$(samplesdir)
@@ -573,6 +578,11 @@ install:
$(INSTALL) -m0644 cbm/*.* $(DESTDIR)$(samplesdir)/cbm
$(INSTALL) -m0644 gamate/*.* $(DESTDIR)$(samplesdir)/gamate
$(INSTALL) -m0644 supervision/*.* $(DESTDIR)$(samplesdir)/supervision
+ $(INSTALL) -m0644 disasm/*.* $(DESTDIR)$(samplesdir)/disasm
+ $(INSTALL) -m0644 kim1/*.* $(DESTDIR)$(samplesdir)/kim1
+ $(INSTALL) -m0644 lynx/*.* $(DESTDIR)$(samplesdir)/lynx
+ $(INSTALL) -m0644 sim65/*.* $(DESTDIR)$(samplesdir)/sim65
+ $(INSTALL) -m0644 sym1/*.* $(DESTDIR)$(samplesdir)/sym1
# --------------------------------------------------------------------------
# Packaging rules
diff --git a/samples/cbm/Makefile b/samples/cbm/Makefile
index 03387a061..4b89722d2 100644
--- a/samples/cbm/Makefile
+++ b/samples/cbm/Makefile
@@ -80,17 +80,19 @@ ifneq ($(filter disk samples.%,$(MAKECMDGOALS)),)
C1541 ?= c1541
endif
-DISK_c64 = samples.d64
+DISK_$(SYS) = samples.d64
EXELIST_c64 = \
fire \
plasma \
- nachtm
+ nachtm \
+ hello
EXELIST_c128 = \
fire \
plasma \
- nachtm
+ nachtm \
+ hello
EXELIST_cbm510 = \
fire \
@@ -101,16 +103,17 @@ EXELIST_cbm610 = \
nachtm
EXELIST_plus4 = \
- plasma
+ plasma \
+ hello
EXELIST_c16 = \
- notavailable
+ hello
EXELIST_pet = \
notavailable
EXELIST_vic20 = \
- notavailable
+ hello
ifneq ($(EXELIST_$(SYS)),)
samples: $(EXELIST_$(SYS))
@@ -135,6 +138,15 @@ plasma: plasma.c
$(CL) -t $(SYS) -O -o plasma -m plasma.map plasma.c
nachtm: nachtm.c
$(CL) -t $(SYS) -O -o nachtm -m nachtm.map nachtm.c
+hello: hello-asm.s
+ # Use separate assembler ...
+ $(AS) -t $(SYS) hello-asm.s
+ # ... and linker commands ...
+ $(LD) -C $(SYS)-asm.cfg -o hello -m hello-asm.map -u __EXEHDR__ hello-asm.o $(SYS).lib
+ @$(DEL) hello-asm.o 2>$(NULLDEV)
+ # ... or compile & link utility
+# $(CL) -C $(SYS)-asm.cfg -o hello -m hello-asm.map -u __EXEHDR__ hello-asm.s
+
# --------------------------------------------------------------------------
# Rule to make a CBM disk with all samples. Needs the c1541 program that comes
diff --git a/samples/cbm/fire.c b/samples/cbm/fire.c
index 40eff0707..1eae086da 100644
--- a/samples/cbm/fire.c
+++ b/samples/cbm/fire.c
@@ -56,7 +56,7 @@
/* Use static local variables for speed */
-#pragma static-locals (1);
+#pragma static-locals (1)
diff --git a/samples/cbm/hello-asm.s b/samples/cbm/hello-asm.s
new file mode 100644
index 000000000..689dcc06b
--- /dev/null
+++ b/samples/cbm/hello-asm.s
@@ -0,0 +1,15 @@
+;
+; Sample assembly program for Commodore machines
+;
+
+ .include "cbm_kernal.inc"
+
+ ldx #$00
+: lda text,x
+ beq out
+ jsr CHROUT
+ inx
+ bne :-
+out: rts
+
+text: .asciiz "hello world!"
diff --git a/samples/cbm/plasma.c b/samples/cbm/plasma.c
index f48d6dc77..5c0b901f7 100644
--- a/samples/cbm/plasma.c
+++ b/samples/cbm/plasma.c
@@ -51,7 +51,7 @@
/* Use static local variables for speed */
-#pragma static-locals (1);
+#pragma static-locals (1)
static const unsigned char sinustable[0x100] = {
diff --git a/samples/checkversion.c b/samples/checkversion.c
index 1d3494a87..f2a9d4a49 100644
--- a/samples/checkversion.c
+++ b/samples/checkversion.c
@@ -10,11 +10,11 @@
#include
#include
-#if ((__CC65__ & 0xff00) > 3) || ((__CC65__ & 0x000f) > 0)
+#if ((__CC65__ >> 8) > 3) || ((__CC65__ & 0x000f) > 0)
/* compiler version is 2.19-git or higher */
# define VER_MAJOR ((__CC65__ >> 8) & 0xff)
# define VER_MINOR (__CC65__ & 0xff)
-#elif ((__CC65__ & 0xff00) == 3)
+#elif ((__CC65__ >> 8) == 3)
/* broken values in version 2.16 - 2.19-git before the bug was fixed */
# define VER_MAJOR 2
# define VER_MINOR (((__CC65__ >> 4) & 0x0f) + 16)
diff --git a/samples/geos/Makefile b/samples/geos/Makefile
index 03f6b8cdc..578927760 100644
--- a/samples/geos/Makefile
+++ b/samples/geos/Makefile
@@ -3,6 +3,9 @@
# var. to build for another target system.
SYS ?= geos-cbm
+# Comes with the VICE emulator, see http://vice-emu.sourceforge.net/
+C1541 ?= c1541
+
# If SYS was given on the commandline, redirect "c64" to "geos-cbm" and
# "apple2enh" to "geos-apple"
ifeq ($(origin SYS),command line)
@@ -82,11 +85,11 @@ samples: $(EXELIST_$(SYS))
$(foreach dir,$(DIRLIST),$(SUBDIR_recipe))
define samples-geos
-c1541 -attach $(0).d64 -geoswrite $(1);
+$(C1541) -attach $(0).d64 -geoswrite $(1);
endef
samples-geos: $(EXELIST_$(SYS))
- c1541 -format "$@,01" d64 $@.d64
+ $(C1541) -format "$@,01" d64 $@.d64
$(foreach tool,$(EXELIST_$(SYS)),$(call samples-geos,$(tool)))
else
samples:
@@ -103,7 +106,7 @@ bitmap.c: logo.pcx
bitmap-demo.cvt: bitmap.c bitmap-demores.grc bitmap-demo.c
$(CL) -t $(SYS) -O -o $@ -m bitmap-demo.map bitmap-demores.grc bitmap-demo.c
-
+
filesel.cvt: fileselres.grc filesel.c
$(CL) -t $(SYS) -O -o $@ -m filesel.map fileselres.grc filesel.c
@@ -134,7 +137,7 @@ vector-demo.cvt: vector-demores.grc vector-demo.c
yesno.cvt: yesnores.grc yesno.c
$(CL) -t $(SYS) -O -o $@ -m yesno.map yesnores.grc yesno.c
-
+
clean:
@$(DEL) overlay-demores.h 2>$(NULLDEV)
@$(DEL) bitmap.c 2>$(NULLDEV)
diff --git a/samples/geos/overlay-demo.c b/samples/geos/overlay-demo.c
index 73ab0e3c0..e3e92e6c2 100644
--- a/samples/geos/overlay-demo.c
+++ b/samples/geos/overlay-demo.c
@@ -27,7 +27,7 @@ void show(char *name)
** rather place the all the code of certain source files into the overlay by
** compiling them with --code-name OVERLAY1.
*/
-#pragma code-name(push, "OVERLAY1");
+#pragma code-name(push, "OVERLAY1")
void foo(void)
{
@@ -39,27 +39,27 @@ void foo(void)
show("One");
}
-#pragma code-name(pop);
+#pragma code-name(pop)
-#pragma code-name(push, "OVERLAY2");
+#pragma code-name(push, "OVERLAY2")
void bar(void)
{
show("Two");
}
-#pragma code-name(pop);
+#pragma code-name(pop)
-#pragma code-name(push, "OVERLAY3");
+#pragma code-name(push, "OVERLAY3")
void foobar (void)
{
show("Three");
}
-#pragma code-name(pop);
+#pragma code-name(pop)
void main(int /*argc*/, char *argv[])
diff --git a/samples/kim1/Makefile b/samples/kim1/Makefile
index 74c415fdc..08bb2a780 100644
--- a/samples/kim1/Makefile
+++ b/samples/kim1/Makefile
@@ -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)
+
+
diff --git a/samples/kim1/font.rom b/samples/kim1/font.rom
new file mode 100644
index 000000000..e69de29bb
diff --git a/samples/kim1/kimGFX.c b/samples/kim1/kimGFX.c
new file mode 100644
index 000000000..45daafa1e
--- /dev/null
+++ b/samples/kim1/kimGFX.c
@@ -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 // For printf
+#include // For rand, srand
+#include // For memcpy
+#include
+
+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;
+}
diff --git a/samples/kim1/kimLife.c b/samples/kim1/kimLife.c
new file mode 100644
index 000000000..fb1696b9e
--- /dev/null
+++ b/samples/kim1/kimLife.c
@@ -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 // For printf
+#include // For rand, srand
+#include // 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;
+}
diff --git a/samples/kim1/kimTest.c b/samples/kim1/kimTest.c
new file mode 100644
index 000000000..273aeb584
--- /dev/null
+++ b/samples/kim1/kimTest.c
@@ -0,0 +1,262 @@
+// --------------------------------------------------------------------------
+// Diagnostics Test for KIM-1
+//
+// Dave Plummer
+// davepl@davepl.com
+//
+// Memory test examples by Michael Barr
+//
+// --------------------------------------------------------------------------
+
+#include // For printf
+#include // For rand, srand
+#include // 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;
+}
+
+
diff --git a/samples/kim1/ramfont.asm b/samples/kim1/ramfont.asm
new file mode 100644
index 000000000..ac0b2c9cd
--- /dev/null
+++ b/samples/kim1/ramfont.asm
@@ -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
diff --git a/samples/kim1/subs.asm b/samples/kim1/subs.asm
new file mode 100644
index 000000000..5b525749e
--- /dev/null
+++ b/samples/kim1/subs.asm
@@ -0,0 +1,1140 @@
+;-----------------------------------------------------------------------------------
+; KIMGFX: Simple pixel graphics for the MOS/Commodore KIM-1
+;-----------------------------------------------------------------------------------
+; (c) Plummer's Software Ltd, 04/25/2023 Created
+; David Plummer
+;-----------------------------------------------------------------------------------
+;
+; File: subs.asm Assembly language subroutines for KIMGFX
+;
+;-----------------------------------------------------------------------------------
+
+
+.SETCPU "6502"
+
+.export _ClearScreen
+.export _ScrollScreen
+.export _SetPixel
+.export _ClearPixel
+.export _DrawCircle
+.export _DrawLine
+.export _AscToPet
+.export _ReverseBits
+.export _DrawChar
+.export _CharOut
+.export _Demo
+.export _Delay
+.export _getch
+
+
+.import _font8x8_basic
+
+; This is the assumed location of the MTU visible memory board's 8K of memory. You can adjust this
+; constant to refelct other locations as needed.
+
+SCREEN = $A000
+
+; Note that even though these constants are defined here and respected, there are still going to be
+; logic assumptions in GetPixelAddress that assume a 320x200 screen. If you change these, you'll
+; need to adjust GetPixelAddress to match.
+
+SCREEN_WIDTH = 320
+SCREEN_HEIGHT = 200
+SCREEN_BYTES = SCREEN_WIDTH * SCREEN_HEIGHT / 8
+CHARWIDTH = 8
+CHARHEIGHT = 8
+BYTESPERROW = (SCREEN_WIDTH / 8)
+BYTESPERCHARROW = (BYTESPERROW * 8)
+CHARSPERROW = (SCREEN_WIDTH / CHARWIDTH)
+ROWSPERCOLUMN = (SCREEN_HEIGHT / CHARHEIGHT)
+LASTROW = SCREEN + SCREEN_BYTES - BYTESPERCHARROW
+
+.segment "ZEROPAGE"
+
+btpt: .res 1
+
+dest:
+dest_lo: .res 1
+dest_hi: .res 1
+
+src:
+src_lo: .res 1
+src_hi: .res 1
+
+adp1:
+adp1_lo: .res 1
+adp1_hi: .res 1
+
+adp2:
+adp2_lo: .res 1
+adp2_hi: .res 1
+
+scroll_src:
+scroll_src_lo: .res 1
+scroll_src_hi: .res 1
+
+scroll_dest:
+scroll_dest_lo: .res 1
+scroll_dest_hi: .res 1
+
+
+.segment "DATA"
+
+; Arguments for graphics functions
+
+_x1cord: .res 2
+_x2cord: .res 2
+_y1cord: .res 2
+_y2cord: .res 2
+_cursorX: .res 1
+_cursorY: .res 1
+
+; Linedraw
+
+dx: .res 2
+dy: .res 2
+e2: .res 2
+sx: .res 1
+sy: .res 1
+dltemp: .res 2
+pixel: .res 1
+
+; DrawCircle
+
+xval: .res 2 ; These could move to zeropage for perf, but presume we
+yval: .res 2 ; we want to minimize the amount we grow zero page use
+err: .res 2
+temp: .res 2
+tempa: .res 1
+tempx: .res 1
+tempy: .res 1
+temp2: .res 2
+x0: .res 2
+y0: .res 2
+
+; CharOut
+
+tempstr: .res 2
+
+.export _x1cord ; Make sure these show up on the C side as zero page
+.export _x2cord
+.export _y1cord
+.export _y2cord
+.export _cursorX
+.export _cursorY
+
+.segment "CODE"
+
+;-----------------------------------------------------------------------------------
+; GetPixelAddress - Calculate the address of a pixel in the video memory
+;-----------------------------------------------------------------------------------
+; Based on MTU PIXADR code
+;-----------------------------------------------------------------------------------
+; In: _x1cord (16-bit)
+; _y1cord (16-bit)
+; Out: adp1 (16-bit) Address of pixel to set
+;-----------------------------------------------------------------------------------
+
+_GetPixelAddress:
+ lda _x1cord ; compute bit address first
+ sta adp1 ; also transfer x1cord to adp1
+ and #$07 ; + which is simply the low 3 bits of x
+ sta btpt
+ lda _x1cord+1 ; finish transferring x1cord to adp1
+ sta adp1+1
+ lsr adp1+1 ; double shift adp1 right 3 to get
+ ror adp1 ; int(xcord/8 )
+ lsr adp1+1
+ ror adp1
+ lsr adp1+1
+ ror adp1
+ sec ; and temporary storage
+ lda _y1cord
+ sta adp2
+ sta temp
+ lda #0
+ sbc _y1cord+1
+ sta adp2+1
+ sta temp+1
+ asl adp2 ; compute 40*(y1cord)
+ rol adp2+1 ; 2*(y1cord)
+ asl adp2
+ rol adp2+1 ; 4*(y1cord)
+ lda adp2 ; add in temporary save of (y1cord)
+ clc ; to make 5*(y1cord)
+ adc temp
+ sta adp2
+ lda adp2+1
+ adc temp+1
+ sta adp2+1 ; 5*(y1cord)
+ asl adp2 ; 10*(1cord)
+ rol adp2+1
+ asl adp2 ; 20#(y1cord)
+ rol adp2+1
+ asl adp2 ; 40*(y1cord)
+ rol adp2+1
+ lda adp2 ; add in int(x1cord/8) computed earlier
+ clc
+ adc adp1
+ sta adp1
+ lda adp2+1
+ adc adp1+1
+ adc #>SCREEN ; add in vmorg*256
+ sta adp1+1 ; final result
+ rts ; return
+
+;-----------------------------------------------------------------------------------
+; Mask tables for individual pixel subroutines
+;
+; MSKTB1 is a table of 1 bits corresponding to bit numbers
+; MSKTB2 is a table of 0 bits corresponding to bit numbers
+;-----------------------------------------------------------------------------------
+
+msktb1: .byte $80,$40,$20,$10,$08,$04,$02,$01
+msktb2: .byte $7F,$BF,$DF,$EF,$F7,$FB,$FD,$FE
+
+_Delay: pha
+ sta temp
+ txa
+ pha
+ tya
+ pha
+
+@loopa: ldx #$ff
+@loopx: ldy #$ff
+@loopy: dey
+ bne @loopy
+ dex
+ bne @loopx
+ dec temp
+ bne @loopa
+
+ pla
+ tay
+ pla
+ tax
+ pla
+ rts
+
+;-----------------------------------------------------------------------------------
+; SetPixel - Set a pixel in the video memory
+;-----------------------------------------------------------------------------------
+; x - _x1cord (16-bit)
+; y - _y1cord (16-bit)
+;-----------------------------------------------------------------------------------
+; Mask tables for individual pixel subroutines
+;-----------------------------------------------------------------------------------
+
+_SetPixel: jsr _GetPixelAddress
+ ldy btpt ; get bit number in y
+ lda msktb1,y ; get a byte with that bit =1, others =0
+ ldy #0
+ ora (adp1),y ; combine the bit with the addressed vm
+ sta (adp1),y ; byte
+ rts
+
+;-----------------------------------------------------------------------------------
+; ClearPixel - Clears a pixel in the video memory
+;-----------------------------------------------------------------------------------
+; x - _x1cord (16-bit)
+; y - _y1cord (16-bit)
+;-----------------------------------------------------------------------------------
+
+_ClearPixel: jsr _GetPixelAddress
+ ldy btpt ; get bit number in y
+ lda msktb2,y ; get a byte with that bit =0, others =1
+ ldy #0
+ and (adp1),y ; remove the bit from the addressed vm
+ sta (adp1),y ; byte
+ rts
+
+;-----------------------------------------------------------------------------------
+; ClearScreen - Clears the entire video memory (and thus the screen)
+;-----------------------------------------------------------------------------------
+
+_ClearScreen:
+ lda #$00
+
+ ldx #SCREEN
+ stx dest_hi
+
+ ldy #0
+: sta (dest), y ; Loop unwound by a factor of 8, which means our iny before the branchh
+ iny ; will still work as it's on a page crossing boundary.
+ sta (dest), y ; This will avoid most of the overhead of the branch.
+ iny
+ sta (dest), y
+ iny
+ sta (dest), y
+ iny
+ sta (dest), y
+ iny
+ sta (dest), y
+ iny
+ sta (dest), y
+ iny
+ sta (dest), y
+ iny
+ bne :-
+
+ inc dest_hi
+ ldx dest_hi
+ cpx #>SCREEN + $20
+ bne :-
+
+ rts
+
+;-----------------------------------------------------------------------------------
+; ScrollScreen - Scrolls the entire video memory (and thus the screen) up one row
+;-----------------------------------------------------------------------------------
+
+BYTES_TO_MOVE = SCREEN_BYTES - BYTESPERCHARROW
+PAGES_TO_MOVE = BYTES_TO_MOVE / 256
+
+_ScrollScreen:
+ pha
+ tya
+ pha
+ txa
+ pha
+
+ ; Load the source (A140) and destination (A000) addresses
+ lda #<(SCREEN+BYTESPERCHARROW)
+ sta scroll_src_lo
+ lda #>(SCREEN+BYTESPERCHARROW)
+ sta scroll_src_hi
+ lda #SCREEN
+ sta scroll_dest_hi
+
+ ldx #PAGES_TO_MOVE
+@outerLoop:
+ ldy #0
+@innerLoop: ;
+ ; I could do this faster in self-modifying code (avoiding the zero page overhead) but then it
+ ; couldn't go into ROM
+
+ lda (scroll_src),y ; I've unwound the loop to do 8 bytes at a time. Since we're doing full pages
+ sta (scroll_dest),y ; as long as we unwind the loop to do 8 bytes at a time, we know we'll still
+ iny ; do the final increment on a page boundary.
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ lda (scroll_src),y
+ sta (scroll_dest),y
+ iny
+ bne @innerLoop ; If Y overflows, it will be 0, so won't branch
+ inc scroll_src_hi
+ inc scroll_dest_hi
+ dex
+ bne @outerLoop
+
+ ; Clear the last line
+ lda #LASTROW
+ sta scroll_dest_hi
+ lda #$00
+ ldy #0
+fullPageLoop:
+ sta (scroll_dest_lo),y
+ iny
+ bne fullPageLoop
+ inc scroll_dest_hi
+partialPageLoop:
+ sta (scroll_dest_lo),y
+ iny
+ cpy #BYTESPERCHARROW - 256 ; Only clear up to the 64th byte (256 + 64 == 320)
+ bne partialPageLoop
+
+ pla
+ tax
+ pla
+ tay
+ pla
+ rts
+
+;-----------------------------------------------------------------------------------
+; DrawCircle - Draws a circle in video memory of a given radius at a given coord
+;-----------------------------------------------------------------------------------
+; x - _x1cord (16-bit)
+; y - _y1cord (16-bit)
+; radius - _y2cord (16-bit)
+;-----------------------------------------------------------------------------------
+; Implements the midpoint circle algorithm without floating point or trig functions
+;-----------------------------------------------------------------------------------
+; int x = radius;
+; int y = 0;
+; int err = 0;
+;
+; while (x >= y)
+; {
+; SETPIXEL(x0 + x, y0 + y, val);
+; SETPIXEL(x0 + y, y0 + x, val);
+; SETPIXEL(x0 - y, y0 + x, val);
+; SETPIXEL(x0 - x, y0 + y, val);
+; SETPIXEL(x0 - x, y0 - y, val);
+; SETPIXEL(x0 - y, y0 - x, val);
+; SETPIXEL(x0 + y, y0 - x, val);
+; SETPIXEL(x0 + x, y0 - y, val);
+;
+; y++;
+; err += 1 + 2 * y;
+; if (2 * (err - x) + 1 > 0) {
+; x--;
+; err += 1 - 2 * x;
+; }
+; }
+;-----------------------------------------------------------------------------------
+
+_DrawCircle: lda _x1cord ; x0 = _x1cord
+ sta x0
+ lda _x1cord+1
+ sta x0+1
+ lda _y1cord ; y0 = _y1cord
+ sta y0
+ lda _y1cord+1
+ sta y0+1
+
+ lda _y2cord ; x = radius
+ sta xval
+ lda _y2cord+1
+ sta xval+1
+
+ lda #$0 ; yval = 0;
+ sta yval
+ sta yval+1
+ sta err ; err = 0;
+ sta err+1
+circleloop:
+ lda xval+1 ; if (xval < yval) we're done;
+ sec
+ cmp yval+1
+ bcc doneCircle ; if high byteof yval is greater, we can draw
+ bne doCircle ; it not greater and not equal, is less, so done
+ lda xval ; in other cases we need to compare the LSB next
+ cmp yval
+ bcs doCircle ; if it's less, but MSB was equal, we go draw
+
+doneCircle: rts
+
+doCircle: lda x0 ; Draw the first of 8 symmetric quadrant copies
+ clc
+ adc yval
+ sta _x1cord
+ lda x0+1
+ adc yval+1
+ sta _x1cord+1
+ lda y0
+ sec
+ sbc xval
+ sta _y1cord
+ lda y0+1
+ sbc xval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 + y, y0 - x, val);
+
+ lda x0
+ sec
+ sbc yval
+ sta _x1cord
+ lda x0+1
+ sbc yval+1
+ sta _x1cord+1
+ lda y0
+ sec
+ sbc xval
+ sta _y1cord
+ lda y0+1
+ sbc xval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 - y, y0 - x, val);
+
+ lda x0
+ sec
+ sbc xval
+ sta _x1cord
+ lda x0+1
+ sbc xval+1
+ sta _x1cord+1
+ lda y0
+ sec
+ sbc yval
+ sta _y1cord
+ lda y0+1
+ sbc yval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 - x, y0 - y, val);
+
+ lda x0
+ sec
+ sbc xval
+ sta _x1cord
+ lda x0+1
+ sbc xval+1
+ sta _x1cord+1
+ lda y0
+ clc
+ adc yval
+ sta _y1cord
+ lda y0+1
+ adc yval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 - x, y0 + y, val);
+
+ lda x0
+ clc
+ adc yval
+ sta _x1cord
+ lda x0+1
+ adc yval+1
+ sta _x1cord+1
+ lda y0
+ clc
+ adc xval
+ sta _y1cord
+ lda y0+1
+ adc xval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 + y, y0 + x, val);
+
+ lda x0
+ clc
+ adc xval
+ sta _x1cord
+ lda x0+1
+ adc xval+1
+ sta _x1cord+1
+ lda y0
+ clc
+ adc yval
+ sta _y1cord
+ lda y0+1
+ adc yval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 + x, y0 + y, val);
+
+ lda x0
+ clc
+ adc xval
+ sta _x1cord
+ lda x0+1
+ adc xval+1
+ sta _x1cord+1
+ lda y0
+ sec
+ sbc yval
+ sta _y1cord
+ lda y0+1
+ sbc yval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 + x, y0 - y, val);
+
+ lda x0
+ sec
+ sbc yval
+ sta _x1cord
+ lda x0+1
+ sbc yval+1
+ sta _x1cord+1
+ lda y0
+ clc
+ adc xval
+ sta _y1cord
+ lda y0+1
+ adc xval+1
+ sta _y1cord+1
+ jsr _SetPixel ; SETPIXEL(x0 - y, y0 + x, val);
+
+ inc yval ; yval++;
+ bne :+
+ inc yval+1
+
+: lda yval ; temp = 2 * yval + 1;
+ asl
+ sta temp
+ lda yval+1
+ rol
+ sta temp+1
+ inc temp
+ bne :+
+ inc temp+1
+:
+ lda err ; err += temp
+ clc
+ adc temp
+ sta err
+ lda err+1
+ adc temp+1
+ sta err+1
+ ; if (2 * (err - xval) + 1 > 0) then dec xval
+ lda err ; temp = err-xval
+ sec
+ sbc xval
+ sta temp
+ lda err+1
+ sbc xval+1
+ sta temp+1
+
+ asl temp ; temp = 2*(err-xval)+1
+ rol temp+1
+ inc temp
+ bne :+
+ inc temp+1
+:
+ lda temp+1 ; if (temp > 0) we'll dec xval
+ bmi doneLoop ; less than zero, so no dec
+ bne decxval ; if not zero, go ahead and dec
+
+ lda temp ; MSB is zero so now check the LSB
+ beq doneLoop ; both bytes are zero, so no dec
+
+decxval: lda xval ; xval--
+ bne :+
+ dec xval+1
+: dec xval
+
+updateerr: lda xval ; temp = xval * 2
+ asl
+ sta temp
+ lda xval+1
+ rol
+ sta temp+1
+
+ lda #1 ; temp2 == 1-temp == 1-(xval*2)
+ sec
+ sbc temp
+ sta temp2
+ lda #0
+ sbc temp+1
+ sta temp2+1
+
+ lda err ; err += 1-(xval*2)
+ clc
+ adc temp2
+ sta err
+ lda err+1
+ adc temp2+1
+ sta err+1
+
+doneLoop: jmp circleloop
+
+;-----------------------------------------------------------------------------------
+; Character set translation tables
+;-----------------------------------------------------------------------------------
+
+ascToPetTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$20,$0d,$11,$93,$0a,$0e,$0f
+ .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f
+ .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f
+ .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f
+ .byte $40,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf
+ .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$5b,$5c,$5d,$5e,$5f
+ .byte $c0,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f
+ .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$db,$dc,$dd,$de,$df
+ .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f
+ .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f
+ .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+ .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+ .byte $60,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f
+ .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$7b,$7c,$7d,$7e,$7f
+ .byte $e0,$e1,$e2,$e3,$e4,$e5,$e6,$e7,$e8,$e9,$ea,$eb,$ec,$ed,$ee,$ef
+ .byte $f0,$f1,$f2,$f3,$f4,$f5,$f6,$f7,$f8,$f9,$fa,$fb,$fc,$fd,$fe,$ff
+
+; PETSCI to Ascii lookup table - not current used, so commented out, but can be used to map fonts
+;
+;
+ petToAscTable: .byte $00,$01,$02,$03,$04,$05,$06,$07,$14,$09,$0d,$11,$93,$0a,$0e,$0f
+ .byte $10,$0b,$12,$13,$08,$15,$16,$17,$18,$19,$1a,$1b,$1c,$1d,$1e,$1f
+ .byte $20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$2a,$2b,$2c,$2d,$2e,$2f
+ .byte $30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3a,$3b,$3c,$3d,$3e,$3f
+ .byte $40,$61,$62,$63,$64,$65,$66,$67,$68,$69,$6a,$6b,$6c,$6d,$6e,$6f
+ .byte $70,$71,$72,$73,$74,$75,$76,$77,$78,$79,$7a,$5b,$5c,$5d,$5e,$5f
+ .byte $c0,$c1,$c2,$c3,$c4,$c5,$c6,$c7,$c8,$c9,$ca,$cb,$cc,$cd,$ce,$cf
+ .byte $d0,$d1,$d2,$d3,$d4,$d5,$d6,$d7,$d8,$d9,$da,$db,$dc,$dd,$de,$df
+ .byte $80,$81,$82,$83,$84,$85,$86,$87,$88,$89,$8a,$8b,$8c,$8d,$8e,$8f
+ .byte $90,$91,$92,$0c,$94,$95,$96,$97,$98,$99,$9a,$9b,$9c,$9d,$9e,$9f
+ .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+ .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+ .byte $60,$41,$42,$43,$44,$45,$46,$47,$48,$49,$4a,$4b,$4c,$4d,$4e,$4f
+ .byte $50,$51,$52,$53,$54,$55,$56,$57,$58,$59,$5a,$7b,$7c,$7d,$7e,$7f
+ .byte $a0,$a1,$a2,$a3,$a4,$a5,$a6,$a7,$a8,$a9,$aa,$ab,$ac,$ad,$ae,$af
+ .byte $b0,$b1,$b2,$b3,$b4,$b5,$b6,$b7,$b8,$b9,$ba,$bb,$bc,$bd,$be,$bf
+
+;-----------------------------------------------------------------------------------
+; PetToAsc - Convert a PETSCII character to ASCII
+;-----------------------------------------------------------------------------------
+; A - Character to convert
+;-----------------------------------------------------------------------------------
+
+_AscToPet: tay
+ lda ascToPetTable, y
+ rts
+
+_PetToAsc: tay
+ lda petToAscTable, Y
+ rts
+
+;-----------------------------------------------------------------------------------
+; ReverseBits - Reverse the bits in a byte
+;-----------------------------------------------------------------------------------
+; A = octet to be reversed
+;-----------------------------------------------------------------------------------
+
+_ReverseBits:
+ ldx #8 ; set counter to 8 (for 8 bits)
+ lda #0 ; initialize A to 0
+ sta temp ; clear result byte (accumulator for the reversed octet)
+: asl ; shift leftmost bit of input into carry
+ lda temp ; load the temporary result byte into A
+ ror ; rotate carry into leftmost bit of result
+ sta temp ; store the updated result back into memory
+ dex ; decrement counter
+ bne :- ; repeat until all bits are processed
+ lda temp ; load the final reversed byte into A
+ rts ; return with result in A
+
+;-----------------------------------------------------------------------------------
+; LoadFont - Makes sure the font data is ready to use. This usually requires
+; reversing the bits so that they match the bit order of the screen
+;-----------------------------------------------------------------------------------
+
+_LoadFont: ldx #3
+ lda #<_font8x8_basic
+ sta adp1_lo
+ lda #>_font8x8_basic
+ sta adp1_hi
+ ldy #0
+@loop: lda (adp1), y
+ jsr _ReverseBits
+ sta (adp1), y
+ iny
+ bne @loop
+
+ inc adp1_lo
+ bne :+
+ inc adp1_hi
+: dex
+ bne @loop
+ rts
+
+ScreenLineAddresses:
+ .word SCREEN + 0 * BYTESPERCHARROW, SCREEN + 1 * BYTESPERCHARROW
+ .word SCREEN + 2 * BYTESPERCHARROW, SCREEN + 3 * BYTESPERCHARROW
+ .word SCREEN + 4 * BYTESPERCHARROW, SCREEN + 5 * BYTESPERCHARROW
+ .word SCREEN + 6 * BYTESPERCHARROW, SCREEN + 7 * BYTESPERCHARROW
+ .word SCREEN + 8 * BYTESPERCHARROW, SCREEN + 9 * BYTESPERCHARROW
+ .word SCREEN + 10 * BYTESPERCHARROW, SCREEN + 11 * BYTESPERCHARROW
+ .word SCREEN + 12 * BYTESPERCHARROW, SCREEN + 13 * BYTESPERCHARROW
+ .word SCREEN + 14 * BYTESPERCHARROW, SCREEN + 15 * BYTESPERCHARROW
+ .word SCREEN + 16 * BYTESPERCHARROW, SCREEN + 17 * BYTESPERCHARROW
+ .word SCREEN + 18 * BYTESPERCHARROW, SCREEN + 19 * BYTESPERCHARROW
+ .word SCREEN + 20 * BYTESPERCHARROW, SCREEN + 21 * BYTESPERCHARROW
+ .word SCREEN + 22 * BYTESPERCHARROW, SCREEN + 23 * BYTESPERCHARROW
+ .word SCREEN + 24 * BYTESPERCHARROW
+ .assert( (* - ScreenLineAddresses) = ROWSPERCOLUMN * 2), error
+
+;-----------------------------------------------------------------------------------
+; DrawChar - Draws an ASCII character at char location x, y
+;-----------------------------------------------------------------------------------
+; 0 <= x < 40
+; 0 <= y < 25
+; Preserves all registers, but its not very threadsafe or reentrant
+;-----------------------------------------------------------------------------------
+
+_DrawChar: sty tempy
+ stx tempx
+ sta tempa
+
+ tya ; Get the address in screen memory where this
+ asl ; character X/Y cursor pos should be drawn
+ tay
+ txa
+ clc
+ adc ScreenLineAddresses, y
+ sta dest_lo
+ lda ScreenLineAddresses+1, y
+ adc #0
+ sta dest_hi
+
+ lda #0 ; Get the address in font memory where this
+ sta src_hi ; Petscii chracter lives (after conversion from
+ lda tempa ; ascii)
+
+ sty temp2
+ jsr _AscToPet
+ ldy temp2
+
+ asl
+ rol src_hi
+ asl
+ rol src_hi
+ asl
+ rol src_hi
+ clc
+ adc #<_font8x8_basic ; Add the base address of the font table to the offset
+ sta src_lo
+ lda src_hi
+ adc #>_font8x8_basic
+ sta src_hi
+
+ ldy #0 ; opy the character def to the screen, one byte at a time
+ ldx #0
+: lda (src), y ; Copy this byte from the character def to the screen target
+ sta (dest, x)
+ lda dest_lo ; Advance to the next "scanline", or pixel row, down
+ clc
+ adc #BYTESPERROW
+ sta dest_hi
+
+ iny
+ cpy #8
+ bne :-
+
+ ldy tempy
+ ldx tempx
+ lda tempa
+ rts
+
+;-----------------------------------------------------------------------------------
+; CursorOn - Turns on the text cursor and draws it at the current cursor pos
+;-----------------------------------------------------------------------------------
+
+CursorOn: ldx _cursorX
+ ldy _cursorY
+ lda #'_'
+ jsr _DrawChar
+ rts
+
+CursorOff: ldx _cursorX
+ ldy _cursorY
+ lda #' '
+ jsr _DrawChar
+ rts
+
+;-----------------------------------------------------------------------------------
+; DrawText - Draws an ASCII char in A at the current cursor pos, saves all regs
+;-----------------------------------------------------------------------------------
+
+_CharOut: sta temp
+ lda #0
+ sta temp+1
+ txa
+ pha
+ tya
+ pha
+
+ ldx #temp
+ jsr _DrawText
+
+ pla
+ tay
+ pla
+ tax
+ rts
+
+;-----------------------------------------------------------------------------------
+; Backspace - Erase the current character and move back one position. Does not
+; move back up to previous line
+;-----------------------------------------------------------------------------------
+
+Backspace: lda _cursorX
+ beq colzero
+ jsr CursorOff
+ dec _cursorX
+ jsr CursorOn
+colzero: rts
+
+;-----------------------------------------------------------------------------------
+; DrawText - Draws an ASCII string at the current cursor position
+;-----------------------------------------------------------------------------------
+; XY - Pointer to the string to draw, stops on NUL or 255 chars later
+;-----------------------------------------------------------------------------------
+
+_DrawText: stx adp1_lo
+ sty adp1_hi
+ jsr CursorOff
+
+ ldy #0
+checkHWrap: lda _cursorX
+ cmp #CHARSPERROW
+ bcc checkVWrap
+ lda #0
+ sta _cursorX
+ inc _cursorY
+
+checkVWrap: lda _cursorY
+ cmp #ROWSPERCOLUMN
+ bcc loadChar
+ jsr _ScrollScreen
+ lda #ROWSPERCOLUMN-1
+ sta _cursorY
+
+loadChar: lda (adp1), y
+ beq doneText
+
+ cmp #$0a
+ bne :+
+
+ lda #0 ; Back to the left edge
+ sta _cursorX
+ inc _cursorY ; Advance to the next line
+ iny
+ bne checkHWrap
+
+: sty temp
+ ldx _cursorX
+ ldy _cursorY
+ jsr _DrawChar
+ ldy temp
+ inc _cursorX
+ iny
+ bne checkHWrap
+
+doneText: jsr CursorOn
+ rts
+
+demoText1: .byte " *** COMMODORE KIM-1 SHELL V0.1 ***", $0A, $0A
+ .byte " 60K RAM SYSTEM. 49152 BYTES FREE.", $0A, $0A, $00
+readyText: .byte $0A,"READY.", $0A, 00
+
+_Demo: jsr _ClearScreen
+ lda #0
+ sta _cursorX
+ sta _cursorY
+ ldx #demoText1
+ jsr _DrawText
+ rts
+
+_Ready: ldx #readyText
+ jsr _DrawText
+ rts
+
+
+;-----------------------------------------------------------------------------------
+; DrawLine - Draws a line between two points
+;-----------------------------------------------------------------------------------
+; _x1cord (16-bit)
+; _y1cord ( 8-bit)
+; _x2cord (16-bit)
+; _y2cord ( 8-bit)
+;-----------------------------------------------------------------------------------
+; Implements something like Bresenham's algorithm for drawing a line:
+;-----------------------------------------------------------------------------------
+; void DrawLine(int x0, int y0, int x1, int y1, byte val)
+; {
+; int dx = abs(_x2cord - _x1cord), sx = _x1cord < _x2cord ? 1 : -1;
+; int dy = abs(_y2cord - _y1cord), sy = _y1cord < _y2cord ? 1 : -1;
+; int err = (dx > dy ? dx : -dy) / 2, e2;
+;
+; while (1)
+; {
+; SETPIXEL(_x1cord, _y1cord, val);
+;
+; if (_x1cord == _x2cord && _y1cord == _y2cord)
+; break;
+;
+; e2 = err;
+;
+; if (e2 > -dx)
+; {
+; err -= dy;
+; _x1cord += sx;
+; }
+; if (e2 < dy)
+; {
+; err += dx;
+; _y1cord += sy;
+; }
+; }
+; }
+;-----------------------------------------------------------------------------------
+
+_DrawLine: sta pixel
+
+ ldx #$01 ; positive x-step for now
+ stx sx
+
+ ; Calculate dx = (x2cord - X1cord) and see if its positive or not
+
+ lda _x2cord ; Calculate dx = (x2cord - X1cord)
+ sec
+ sbc _x1cord
+ sta dx
+ lda _x2cord+1
+ sbc _x1cord+1
+ sta dx+1
+ bpl calcdy ; dx is positive (dx >= 0), so we're good
+
+ ; dx was negative (dx < 0), so we set sx to -1 and get the absolute
+ ; value by subtracting the other direction
+
+ ldx #$FF ; negative x-step
+ stx sx
+ lda _x1cord ; Calculate dx = (x2cord - X1cord)
+ sec
+ sbc _x2cord
+ sta dx
+ lda _x1cord+1
+ sbc _x2cord+1
+ sta dx+1
+
+ ; Calculate dy = (y2cord - y1cord) and see if its positive or not
+
+calcdy: ldx #$01 ; positive y-step for now
+ stx sy
+ lda _y2cord
+ sec
+ sbc _y1cord
+ sta dy
+ bcs positivedy ; If y2cord > y1cord, then dy is positive and we're good
+
+ ; dy was negative (dy < 0), so we set sy to -1 and get the absolute value
+
+ ldx #$FF ; negative y-step
+ stx sy
+ lda _y1cord
+ sec
+ sbc _y2cord
+ sta dy
+
+ ; Now we have dx and dy, so we can calculate err, but first we need
+ ; to see if dx > dy or not
+
+positivedy: lda dx+1 ; Check if dx > dy (both are always positive now)
+ bne dxgt ; If MSB of dx is greater than zero, then dx > dy since dy is 8-bits
+ lda dy
+ cmp dx
+ bcs dygte
+
+dxgt: lda dx ; We found dx>dy so set err = dx / 2
+ sta err
+ lda dx+1
+ lsr
+ sta err+1 ; err = dx/2
+ ror err
+ jmp loop
+
+dygte: lda #0 ; we found dx <= dy so set err = -dy / 2
+ sec
+ sbc dy ; else err = -dy / 2
+ ror
+ ora #$80
+ sta err
+ lda #$FF
+ sta err+1
+
+ ; Now we have dx, dy, and err, so we can start drawing pixels
+
+loop: lda pixel
+ beq clearpixel
+ jsr _SetPixel ; Plot the current _x1cord, _y1cord
+ jmp next
+clearpixel: jsr _ClearPixel ; Clear the current _x1cord, _y1cord
+
+next: lda _x1cord ; if (_x1cord == _x2cord && _y1cord == _y2cord) then we rts
+ cmp _x2cord
+ bne noteq
+ lda _y1cord
+ cmp _y2cord
+ bne noteq
+ lda _x1cord+1
+ cmp _x2cord+1
+ bne noteq
+
+ rts
+
+noteq: lda err ; e2 = err
+ sta e2
+ lda err+1
+ sta e2+1
+
+ ; Check the two update conditions for x and y, and update if needed
+
+ lda e2 ; if (e2 > -dx) is the same as if (e2 + dx > 0), so we test that because its easier
+ clc ; If its true then we dec err and inc _x1cord
+ adc dx
+ sta temp
+ lda e2+1
+ adc dx+1
+ bmi doneupdatex ; If result is negative, then e2 + dx < 0, so we don't dec err or inc _x1cord
+ bne stepx ; If MSB is non-zero, then e2 + dx > 0, so we DO dec err and inc _x1cord
+ lda temp ; If result is zero in MSB, then we check the LSB here
+ beq doneupdatex ; If LSB is zero, then we don't dec err or inc _x1cord
+ ; We already know e2 + dx > 0, so LSB can't be negative
+stepx: lda sx
+ bmi decx
+incxval: inc _x1cord ; _x1cord += 1 because sx == 1
+ bne updatexerr
+ inc _x1cord+1
+ jmp updatexerr
+
+decx: lda _x1cord ; _x1cord += 1 because sx == 1
+ sec
+ sbc #1
+ sta _x1cord
+ lda _x1cord+1
+ sbc #0
+ sta _x1cord+1
+
+updatexerr: lda err ; err -= dy
+ sec
+ sbc dy
+ sta err
+ lda err+1
+ sbc #0
+ sta err+1
+
+doneupdatex: lda e2+1 ; if (e2 < dy) then we inc err and inc _y1cord
+ bmi updateerry ; If MSB is negative, then e2 < dy, so we inc err and inc _y1cord
+ bne noupdatey ; If the MSB of e2 is set and positive, then we know e2 > dy, so we don't inc err or inc _y1cord
+ lda e2
+ sec
+ sbc dy
+ beq noupdatey ; e2 - dy == 0 so we don't inc err or inc _y1cord
+ bcs noupdatey ; if e2 was large enough that carry never cleared, then e2 > dy do no update
+
+updateerry: lda err ; err += dx
+ clc
+ adc dx
+ sta err
+ lda err+1
+ adc dx+1
+ sta err+1
+
+stepy: lda _y1cord
+ clc
+ adc sy
+ sta _y1cord
+
+noupdatey: jmp loop
+
+_getch: jsr $1E5A ; Get character using Monitor ROM call
+ and #$7F ; Clear top bit
+ cmp #$0D ; Check for '\r'
+ bne gotch ; ...if CR character
+ lda #$0A ; Replace with '\n'
+gotch: rts
diff --git a/samples/lynx/mandelbrot.c b/samples/lynx/mandelbrot.c
index ce49fbf7a..27ac7d340 100644
--- a/samples/lynx/mandelbrot.c
+++ b/samples/lynx/mandelbrot.c
@@ -26,7 +26,7 @@
#define divfp(_a,_b) ((((signed long)_a)<'; or, set a SYS env.
+# var. to build for another target system.
+SYS ?= sim6502
+
+# Just the usual way to find out if we're
+# using cmd.exe to execute make rules.
+ifneq ($(shell echo),)
+ CMD_EXE = 1
+endif
+
+ifdef CMD_EXE
+ NULLDEV = nul:
+ DEL = -del /f
+ RMDIR = rmdir /s /q
+else
+ NULLDEV = /dev/null
+ DEL = $(RM)
+ RMDIR = $(RM) -r
+endif
+
+ifdef CC65_HOME
+ AS = $(CC65_HOME)/bin/ca65
+ CC = $(CC65_HOME)/bin/cc65
+ CL = $(CC65_HOME)/bin/cl65
+ LD = $(CC65_HOME)/bin/ld65
+else
+ AS := $(if $(wildcard ../../bin/ca65*),../../bin/ca65,ca65)
+ CC := $(if $(wildcard ../../bin/cc65*),../../bin/cc65,cc65)
+ CL := $(if $(wildcard ../../bin/cl65*),../../bin/cl65,cl65)
+ LD := $(if $(wildcard ../../bin/ld65*),../../bin/ld65,ld65)
+endif
+
+EXELIST_sim6502 = \
+ cpumode_example.bin \
+ timer_example.bin \
+ trace_example.bin
+
+ifneq ($(EXELIST_$(SYS)),)
+samples: $(EXELIST_$(SYS))
+else
+samples: notavailable
+endif
+
+# empty target used to skip systems that will not work with any program in this dir
+notavailable:
+ifeq ($(MAKELEVEL),0)
+ @echo "info: sim65 tests not available for" $(SYS)
+else
+# suppress the "nothing to be done for 'samples' message
+ @echo > $(NULLDEV)
+endif
+
+.SUFFIXES:
+.SUFFIXES: .c .bin
+
+%.bin : %.c
+ $(CL) -t $(SYS) -Oris -m $*.map -o $@ $<
+
+clean:
+ @$(DEL) *.o *.map *.bin 2>$(NULLDEV)
diff --git a/samples/sim65/cpumode_example.c b/samples/sim65/cpumode_example.c
new file mode 100644
index 000000000..6cc2d0c16
--- /dev/null
+++ b/samples/sim65/cpumode_example.c
@@ -0,0 +1,104 @@
+/*
+ * Sim65 cpu-mode switching example.
+ *
+ * Description
+ * -----------
+ *
+ * We can inspect and manipulate the CPU model that sim65 emulates at runtime.
+ *
+ * Sim65 always runs in one of three modes:
+ *
+ * - 6502 mode: the 151 documented opcodes are supported; if the processor encounters
+ * one of the 105 undocumented opcodes, the simulator ends with an
+ * 'illegal opcode' message.
+ * - 65C02 mode: the 105 undocumented opcodes now have well-defined behavior. Some
+ * do useful things, while all others are now defined as NOPs.
+ * - 6502X mode: the 105 undocumented opcodes don't have documented behavior, but
+ * they /do/ have behavior on a real 6502. This behavior has been
+ * figured out, and is deterministic (with minor exceptions).
+ * In this mode, sim65 mimics the behavior of a real 6502 when
+ * it encounters an undocumented opcode, rather than terminating.
+ *
+ * In the example below, we first switch to 6502X mode and execute a small
+ * assembly code fragment, then repeat this in 65C02 mode.
+ *
+ * The code fragment is designed to distinguish between a 6502 and a 65C02
+ * processor based on the behavior of the ADC function in decimal mode.
+ *
+ * Important Note:
+ *
+ * When running in a program compiled for the "sim6502" target, it is safe to switch to
+ * 65C02 or 6502X mode, since the runtime library will only use plain 6502 opcodes, and
+ * those work the same in 65C02 and 6502X mode.
+ *
+ * However, when running in a program compiled for the "sim65c02" target, it is NOT safe
+ * to switch to 6502 or 6502X mode, since many routines in the runtime library use
+ * 65C02-specific opcodes, and these will not work as expected when the CPU is switched
+ * to 6502 or 6502X mode. When such an instruction is encountered, the program will
+ * exhibit undefined behavior.
+ *
+ * For this reason, this program will only work when compiled for the "sim6502" target.
+ *
+ * Running the example
+ * -------------------
+ *
+ * cl65 -t sim6502 -O cpumode_example.c -o cpumode_example.prg
+ * sim65 cpumode_example.prg
+ *
+ */
+
+#include
+#include
+#include
+
+static bool __fastcall__ is_65c02(void)
+{
+ /* This assembly routine loads 0 into AX on a 6502 (also on a 6502 on which decimal
+ * mode is not implemented), and 1 on a 65C02.
+ *
+ * Note: this implementation triggers a "control reaches end of non-void function"
+ * warning that can be safely ignored. While no return statement is present, the
+ * return value is correctly loaded into AX by the assembly code.
+ */
+ __asm__("sed");
+ __asm__("ldx #0");
+ __asm__("txa");
+ __asm__("sbc #155");
+ __asm__("asl");
+ __asm__("rol");
+ __asm__("and #1");
+ __asm__("cld");
+}
+
+int main(void)
+{
+ printf("CPU mode at startup ....... : %u\n", GET_CPU_MODE());
+ printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
+
+ printf("\n");
+
+ printf("Switching to 6502 mode ....\n");
+ SET_CPU_MODE(SIM65_CPU_MODE_6502);
+ printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
+ printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
+
+ printf("\n");
+
+ printf("Switching to 65C02 mode ...\n");
+ SET_CPU_MODE(SIM65_CPU_MODE_65C02);
+ printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
+ printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
+
+ printf("\n");
+
+ printf("Switching to 6502X mode ...\n");
+ SET_CPU_MODE(SIM65_CPU_MODE_6502X);
+ printf("Current CPU mode .......... : %u\n", GET_CPU_MODE());
+ printf("Is 65C02? ................. : %s\n", is_65c02() ? "YES" : "NO");
+
+ printf("\n");
+
+ printf("Bye!\n");
+
+ return 0;
+}
diff --git a/samples/sim65/timer_example.c b/samples/sim65/timer_example.c
new file mode 100644
index 000000000..8262bdff8
--- /dev/null
+++ b/samples/sim65/timer_example.c
@@ -0,0 +1,117 @@
+/*
+ * Sim65 timer example.
+ *
+ * Description
+ * -----------
+ *
+ * This example tests the clock cycle counter feature of sim65.
+ *
+ * The function 'timestamp' obtains the lower 32-bits of the clock cycle counter.
+ *
+ * The function 'calc_sum_terms' calculates the sum of a range of integers
+ * starting at zero. It simply iterates over all terms, which means that its
+ * runtime is a linear function of its input value.
+ *
+ * In the main function, we first derive an 'offset' value by getting two timestamp
+ * values, with nothing happening in between. Ideally this should yield a 0 clock
+ * cycle duration, but due to the overhead of calling the 'timestamp' function,
+ * and the 'timestamp' function itself, the difference between these timestamp
+ * will be non-zero. We store this value in the 'overhead' variable, and subtract
+ * this value in later measurements.
+ *
+ * Next, we measure the duration of calling the function 'calc_sum_terms' with two
+ * input values, 0, and 1. The duration includes storing the result in the 'result'
+ * variable.
+ *
+ * Extrapolating from these two measurements, and assuming that the runtime of
+ * calling 'calc_sum_terms' and storing its result scales linearly with its argument,
+ * we can predict the duration of a call to 'calc_sum_terms' with a much larger
+ * argument (max_terms = 10000).
+ *
+ * Finally, we actually measure the duration with max_terms = 10000. If the
+ * duration measured is equal to the predicted value, we exit successfully. If not,
+ * we exit with failure.
+ *
+ * Running the example
+ * -------------------
+ *
+ * cl65 -t sim6502 -O timer_example.c -o timer_example.prg
+ * sim65 timer_example.prg
+ *
+ */
+
+#include
+#include
+
+static uint32_t timestamp(void)
+{
+ peripherals.counter.select = COUNTER_SELECT_CLOCKCYCLE_COUNTER;
+ peripherals.counter.latch = 0;
+ return peripherals.counter.value32[0];
+}
+
+static unsigned long calc_sum_terms(unsigned max_term)
+/* A function with a runtime that scales linearly with its argument. */
+{
+ unsigned k;
+ unsigned long sum = 0;
+ for (k = 0; k <= max_term; ++k)
+ {
+ sum += k;
+ }
+ return sum;
+}
+
+int main(void)
+{
+ unsigned max_term;
+ unsigned long result;
+ uint32_t t1, t2, overhead;
+ int32_t d0, d1, duration;
+ int32_t predicted_duration;
+
+ /* Calibration measurement of zero clock cycles, to determine the overhead. */
+
+ overhead = 0;
+ t1 = timestamp();
+ t2 = timestamp() - overhead;
+ overhead = (t2 - t1);
+
+ /* Calculate call duration (including assignment of result) for argument value 0. */
+
+ max_term = 0;
+ t1 = timestamp();
+ result = calc_sum_terms(max_term);
+ t2 = timestamp();
+ d0 = (t2 - t1) - overhead;
+ printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d0);
+
+ /* Calculate call duration (including assignment of result) for argument value 1. */
+
+ max_term = 1;
+ t1 = timestamp();
+ result = calc_sum_terms(max_term);
+ t2 = timestamp();
+ d1 = (t2 - t1) - overhead;
+ printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, d1);
+
+ /* Predict runtime for a much bigger argument value, 10000. */
+
+ max_term = 10000;
+ predicted_duration = d0 + max_term * (d1 - d0);
+
+ printf("predicted duration for max_term = %u: %ld\n", max_term, predicted_duration);
+
+ /* Do the actual measurement for max_term = 10000.
+ * Note: equality between the prediction and the measurement is only achieved if we compile with -O.
+ */
+
+ t1 = timestamp();
+ result = calc_sum_terms(max_term);
+ t2 = timestamp();
+ duration = (t2 - t1) - overhead;
+ printf("max_term = %u -> result = %lu; duration = %ld\n", max_term, result, duration);
+
+
+ return 0;
+}
diff --git a/samples/sim65/trace_example.c b/samples/sim65/trace_example.c
new file mode 100644
index 000000000..cd4ec87d7
--- /dev/null
+++ b/samples/sim65/trace_example.c
@@ -0,0 +1,40 @@
+/*
+ * Sim65 trace functionailty example.
+ *
+ * Description
+ * -----------
+ *
+ * The easiest way to use tracing in sim65 is to pass the '--trace' option
+ * to sim65 while starting a program.
+ *
+ * However, it is also possiblke to enable and disable the trace functionality
+ * at runtime, from within the C code itself. This can be useful to produce
+ * runtime traces of small code fragments for debugging purposes.
+ *
+ * In this example, We use the TRACE_ON and TRACE_OFF macros provided in sim65.h
+ * to trace what the CPU is doing during a single statement: the assignment of
+ * a constant to a global variable.
+ *
+ * Running the example
+ * -------------------
+ *
+ * cl65 -t sim6502 -O trace_example.c -o trace_example.prg
+ * sim65 trace_example.prg
+ *
+ * Compiling and running the program like this will produce a trace of six 6502 instructions.
+ * The first four instructions correspond to the 'x = 0x1234' assignment statement.
+ * The last two instructions (ending in a store to address $FFCB) disable the trace facility.
+ *
+ */
+
+#include
+
+unsigned x;
+
+int main(void)
+{
+ TRACE_ON();
+ x = 0x1234;
+ TRACE_OFF();
+ return 0;
+}
diff --git a/src/Makefile b/src/Makefile
index 034a2230f..c5d91ce5a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -168,4 +168,19 @@ $(eval $(call OBJS_template,common))
$(foreach prog,$(PROGS),$(eval $(call PROG_template,$(prog))))
+
+.PHONY: dbginfo dbgsh test
+
+test: dbginfo dbgsh
+
+$(eval $(call OBJS_template,dbginfo))
+
+dbginfo: $(dbginfo_OBJS)
+
+../wrk/dbgsh$(EXE_SUFFIX): $(dbginfo_OBJS) ../wrk/common/common.a
+ $(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
+
+dbgsh: ../wrk/dbgsh$(EXE_SUFFIX)
+
+
-include $(DEPS)
diff --git a/src/ar65.vcxproj b/src/ar65.vcxproj
index 27d12dadc..0fd788e06 100644
--- a/src/ar65.vcxproj
+++ b/src/ar65.vcxproj
@@ -5,10 +5,18 @@
Debug
Win32
+
+ Debug
+ x64
+
Release
Win32
+
+ Release
+ x64
+
{5E8C19C6-B167-440C-8BEF-3CBF109CDB49}
@@ -21,15 +29,22 @@
true
+
+ true
+
false
+
+ false
+
+
@@ -40,6 +55,15 @@
Console
+
+
+ _CONSOLE;_DEBUG;%(PreprocessorDefinitions)
+ 4267;%(DisableSpecificWarnings)
+
+
+ Console
+
+
_CONSOLE;NDEBUG;%(PreprocessorDefinitions)
@@ -48,6 +72,15 @@
Console
+
+
+ _CONSOLE;NDEBUG;%(PreprocessorDefinitions)
+ 4267;%(DisableSpecificWarnings)
+
+
+ Console
+
+
diff --git a/src/ca65.vcxproj b/src/ca65.vcxproj
index 3cc6019f2..d8fd39303 100644
--- a/src/ca65.vcxproj
+++ b/src/ca65.vcxproj
@@ -5,10 +5,18 @@
Debug
Win32
+
+ Debug
+ x64
+
Release
Win32
+
+ Release
+ x64
+
{D28CB737-E6CA-49C4-8CE9-FF05F86DD4EC}
@@ -21,15 +29,22 @@
true
+
+ true
+
false
+
+ false
+
+
@@ -40,6 +55,15 @@
Console
+
+
+