Merge branch 'master' into coniopeek

This commit is contained in:
mrdudz
2025-05-24 17:52:58 +02:00
604 changed files with 32401 additions and 8345 deletions

View File

@@ -33,6 +33,7 @@ TARGETS = apple2 \
none \
osic1p \
pce \
rp6502 \
sim6502 \
sim65c02 \
supervision \
@@ -131,8 +132,8 @@ $(TARGETS): | ../lib
else # TARGET
CA65FLAGS =
CC65FLAGS = -Or -W error
CA65FLAGS = -g
CC65FLAGS = -g -Or -W error
EXTZP = cbm510 \
cbm610 \

380
libsrc/NameClashes.md Normal file
View File

@@ -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

View File

@@ -0,0 +1,23 @@
;
; Oliver Schmidt, 2024-08-06
;
; unsigned char __fastcall__ allow_lowercase (unsigned char onoff);
;
.export _allow_lowercase
.import uppercasemask, return0, 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

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

@@ -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

75
libsrc/apple2/callmain.s Normal file
View File

@@ -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
lda #>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

View File

@@ -9,6 +9,7 @@
.export _cgetc
.import cursor, putchardirect
.include "zeropage.inc"
.include "apple2.inc"
_cgetc:
@@ -22,7 +23,7 @@ _cgetc:
.else
lda #' ' | $40 ; Blank, flashing
.endif
jsr putchardirect ; Returns old character in X
jsr putchardirect ; Saves old character in tmp3
; Wait for keyboard strobe.
: inc RNDL ; Increment random counter low
@@ -37,7 +38,7 @@ _cgetc:
; Restore old character.
pha
txa
lda tmp3
jsr putchardirect
pla

View File

@@ -1,57 +0,0 @@
/*****************************************************************************/
/* */
/* closedir.c */
/* */
/* Close a directory */
/* */
/* */
/* */
/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include "dir.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
int __fastcall__ closedir (DIR* dir)
{
int result;
/* Cleanup directory file */
result = close (dir->fd);
/* Cleanup DIR */
free (dir);
return result;
}

35
libsrc/apple2/closedir.s Normal file
View File

@@ -0,0 +1,35 @@
;
; Colin Leroy-Mira <colin@colino.net>, 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

View File

@@ -11,18 +11,23 @@
_cpeekc:
ldy CH
.ifdef __APPLE2ENH__
sec ; Assume main memory
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
bcs :+ ; In main memory
bit LOWSCR
plp
: .endif
eor #$80 ; Invert high bit
ldx #$00
ldx #>$0000
rts

View File

@@ -11,9 +11,15 @@
.export _cputcxy, _cputc
.export cputdirect, newline, putchar, putchardirect
.import gotoxy, VTABZ
.ifndef __APPLE2ENH__
.import uppercasemask
.endif
.include "zeropage.inc"
.include "apple2.inc"
.macpack cpu
.segment "ONCE"
.ifdef __APPLE2ENH__
@@ -33,7 +39,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
@@ -41,19 +47,34 @@ _cputc:
.ifndef __APPLE2ENH__
cmp #$E0 ; Test for lowercase
bcc cputdirect
and #$DF ; Convert to uppercase
and uppercasemask
.endif
cputdirect:
jsr putchar
.ifdef __APPLE2ENH__
bit RD80VID ; In 80 column mode?
bpl :+
inc OURCH ; Bump to next column
lda OURCH
bra check ; Must leave CH alone
: .endif
inc CH ; Bump to next column
lda CH
cmp WNDWDTH
bcc :+
check: cmp WNDWDTH
bcc done
jsr newline
left: lda #$00 ; Goto left edge of screen
left:
.ifdef __APPLE2ENH__
stz CH ; Goto left edge of screen
bit RD80VID ; In 80 column mode?
bpl done
stz OURCH ; Goto left edge of screen
.else
lda #$00 ; Goto left edge of screen
sta CH
: rts
.endif
done: rts
newline:
inc CV ; Bump to next line
@@ -77,22 +98,27 @@ putchar:
mask: and INVFLG ; Apply normal, inverse, flash
putchardirect:
pha
tax
ldy CH
.ifdef __APPLE2ENH__
sec ; Assume main memory
bit RD80VID ; In 80 column mode?
bpl put ; No, just go ahead
tya
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
.endif
put: lda (BASL),Y ; Get current character
tax ; Return old character for _cgetc
pla
sta tmp3 ; Save old character for _cgetc
txa
sta (BASL),Y
.ifdef __APPLE2ENH__
bit LOWSCR ; Doesn't hurt in 40 column mode
.endif
bcs :+ ; In main memory
bit LOWSCR
plp
: .endif
rts

View File

@@ -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
lda #>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

View File

@@ -23,5 +23,5 @@ _getcurrentdevice:
bne :+
lda #$FF ; INVALID_DEVICE
: ldx #$00
: ldx #>$0000
rts

View File

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

View File

@@ -31,5 +31,5 @@ diocommon:
dioepilog:
; Return success or error
sta ___oserror
ldx #$00
ldx #>$0000
rts

View File

@@ -1,62 +0,0 @@
/*****************************************************************************/
/* */
/* dir.h */
/* */
/* Apple ][ system specific DIR */
/* */
/* */
/* */
/* (C) 2005 Oliver Schmidt, <ol.sc@web.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. */
/* */
/*****************************************************************************/
#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

15
libsrc/apple2/dir.inc Normal file
View File

@@ -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

View File

@@ -0,0 +1,24 @@
;
; Colin Leroy-Mira <colin@colino.net>, 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

View File

@@ -5,8 +5,8 @@
;
.export _exec
.import pushname, popname
.import popax, done, _exit
.import mli_file_info_direct
.import pushname, popname, popax, done, _exit
.include "zeropage.inc"
.include "errno.inc"
@@ -17,13 +17,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 +31,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 +48,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,35 +111,9 @@ setbuf: lda #$00 ; Low byte
dex
dex
; Set I/O buffer
sta mliparam + MLI::OPEN::IO_BUFFER
stx mliparam + MLI::OPEN::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
; Set OPEN MLI call I/O buffer parameter
sta io_buffer
stx io_buffer+1
.ifdef __APPLE2ENH__
; Calling the 80 column firmware needs the ROM switched
@@ -194,14 +158,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
@@ -213,8 +188,6 @@ source: jsr $BF00
bcs error
; Check for cmdline handling
lda $0100 ; Valid cmdline?
beq jump ; No, jump to program right away
ldx file_type ; SYS file?
bne system ; Yes, check for startup filename
@@ -256,6 +229,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
@@ -287,4 +268,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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

189
libsrc/apple2/get_tv.s Normal file
View File

@@ -0,0 +1,189 @@
;
; Colin Leroy-Mira <colin@colino.net>, 2025
;
; unsigned char __fastcall__ get_tv(void)
;
.export _get_tv
.import _set_iigs_speed, _get_iigs_speed
.import ostype
.constructor calibrate_tv, 2
.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

View File

@@ -30,5 +30,5 @@ next: inx
bne next
done: txa
ldx #$00
ldx #>$0000
rts

View File

@@ -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
ldx #>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
ldx #>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

73
libsrc/apple2/gmtime_dt.s Normal file
View File

@@ -0,0 +1,73 @@
;
; Oliver Schmidt, 14.08.2018
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
; 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 ; Return pointer to tm_buf
ldx #>tm_buf
rts
; Load errno code and return NULL
erange: lda #ERANGE
sta ___errno
lda #$00
tax
rts
.bss
tm_buf:
.tag tm

View File

@@ -22,4 +22,9 @@ _gotoxy:
_gotox:
sta CH ; Store X
.ifdef __APPLE2ENH__
bit RD80VID ; In 80 column mode?
bpl :+
sta OURCH ; Store X
: .endif
rts

View File

@@ -3,22 +3,37 @@
;
.export initcwd
.import __cwd
.import __cwd, __dos_type
.include "zeropage.inc"
.include "apple2.inc"
.include "mli.inc"
initcwd:
; Set static prefix buffer
lda #<__cwd
ldx #>__cwd
sta mliparam + MLI::PREFIX::PATHNAME
stx mliparam + MLI::PREFIX::PATHNAME+1
; Check for ProDOS 8
lda __dos_type
beq done
; Get current working directory
lda #GET_PREFIX_CALL
ldx #PREFIX_COUNT
jsr callmli
; Save random counter
lda RNDL
pha
lda RNDH
pha
; Call MLI
; We're not using mli.s' callmli because its
; mliparam is in BSS and this will be called
; before LC code is moved to the Language Card.
jsr $BF00 ; MLI call entry point
.byte GET_PREFIX_CALL ; MLI command
.addr mli_parameters ; MLI parameter
; Restore random counter
pla
sta RNDH
pla
sta RNDL
; Check for null prefix
ldx __cwd
@@ -39,3 +54,9 @@ initcwd:
sta __cwd,x
done: rts
.rodata
mli_parameters:
.byte $01 ; Number of parameters
.addr __cwd ; Address of parameter

View File

@@ -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

View File

@@ -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

37
libsrc/apple2/mktime_dt.s Normal file
View File

@@ -0,0 +1,37 @@
;
; Oliver Schmidt, 14.08.2018
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
; 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
ldx #>tm_buf
jmp _mktime
err: lda #$00
tax
sta sreg
sta sreg+1
rts

View File

@@ -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

View File

@@ -0,0 +1,33 @@
;
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
.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

View File

@@ -0,0 +1,22 @@
;
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
.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

View File

@@ -155,6 +155,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
@@ -211,7 +212,7 @@ next: inc ptr1+1
common: jsr firmware
; Enable interrupts and return success
cli
plp
lda #<MOUSE_ERR_OK
ldx #>MOUSE_ERR_OK
rts
@@ -220,6 +221,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 +251,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 +301,7 @@ GETBOX:
; the screen). No return code required.
MOVE:
ldy slot
php
sei
; Set y
@@ -328,9 +332,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 +344,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 +366,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

View File

@@ -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

View File

@@ -1,112 +0,0 @@
/*****************************************************************************/
/* */
/* opendir.h */
/* */
/* Open a directory */
/* */
/* */
/* */
/* (C) 2005 Oliver Schmidt, <ol.sc@web.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 <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#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;
}

159
libsrc/apple2/opendir.s Normal file
View File

@@ -0,0 +1,159 @@
;
; Colin Leroy-Mira <colin@colino.net>, 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
clc
adc ptr1
pha
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

View File

@@ -23,7 +23,7 @@ ___osmaperrno:
; Found the code
: lda ErrTab-1,x
ldx #$00 ; High byte always zero
ldx #>$0000
rts
.rodata

View File

@@ -1,90 +0,0 @@
/*****************************************************************************/
/* */
/* readdir.c */
/* */
/* Read directory entry */
/* */
/* */
/* */
/* (C) 2005 Oliver Schmidt, <ol.sc@web.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 <stddef.h>
#include <unistd.h>
#include <dirent.h>
#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];
}

113
libsrc/apple2/readdir.s Normal file
View File

@@ -0,0 +1,113 @@
;
; Colin Leroy-Mira <colin@colino.net>, 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

View File

@@ -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

View File

@@ -1,69 +0,0 @@
/*****************************************************************************/
/* */
/* rewinddir.c */
/* */
/* Reset directory stream */
/* */
/* */
/* */
/* (C) 2005 Oliver Schmidt, <ol.sc@web.de> */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
/* warranty. In no event will the authors be held liable for any damages */
/* arising from the use of this software. */
/* */
/* Permission is granted to anyone to use this software for any purpose, */
/* including commercial applications, and to alter it and redistribute it */
/* freely, subject to the following restrictions: */
/* */
/* 1. The origin of this software must not be misrepresented; you must not */
/* claim that you wrote the original software. If you use this software */
/* in a product, an acknowledgment in the product documentation would be */
/* appreciated but is not required. */
/* 2. Altered source versions must be plainly marked as such, and must not */
/* be misrepresented as being the original software. */
/* 3. This notice may not be removed or altered from any source */
/* distribution. */
/* */
/*****************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include "dir.h"
/*****************************************************************************/
/* Code */
/*****************************************************************************/
void __fastcall__ rewinddir (register DIR* dir)
{
/* Rewind directory file */
if (lseek (dir->fd, 0, SEEK_SET)) {
/* Read directory key block */
if (read (dir->fd,
dir->block.bytes,
sizeof (dir->block)) == sizeof (dir->block)) {
/* Skip directory header entry */
dir->current_entry = 1;
/* Return success */
return;
}
}
/* Assert that no subsequent readdir() finds an active entry */
memset (dir->block.bytes, 0, sizeof (dir->block));
/* Return failure */
}

70
libsrc/apple2/rewinddir.s Normal file
View File

@@ -0,0 +1,70 @@
;
; Colin Leroy-Mira <colin@colino.net>, 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

752
libsrc/apple2/ser/a2.gs.s Normal file
View File

@@ -0,0 +1,752 @@
;
; Serial driver for the Apple IIgs Zilog Z8530.
;
; Colin Leroy-Mira <colin@colino.net>, 2023
;
; This software is licensed under the same license as cc65,
; the zlib license (see LICENSE file).
;
; Documentation from http://www.applelogic.org/files/Z8530UM.pdf (pages
; referred to where applicable)
; and https://gswv.apple2.org.za/a2zine/Utils/Z8530_SCCsamples_info.txt
.setcpu "65816"
.include "zeropage.inc"
.include "ser-kernel.inc"
.include "ser-error.inc"
.macpack module
; ------------------------------------------------------------------------
; Header. Includes jump table
.ifdef __APPLE2ENH__
module_header _a2e_gs_ser
.else
module_header _a2_gs_ser
.endif
; Driver signature
.byte $73, $65, $72 ; "ser"
.byte SER_API_VERSION ; Serial API version number
; Library reference
.addr $0000
; Jump table
.addr SER_INSTALL
.addr SER_UNINSTALL
.addr SER_OPEN
.addr SER_CLOSE
.addr SER_GET
.addr SER_PUT
.addr SER_STATUS
.addr SER_IOCTL
.addr SER_IRQ
;----------------------------------------------------------------------------
; Global variables
.bss
RecvHead: .res 1 ; Head of receive buffer
RecvTail: .res 1 ; Tail of receive buffer
RecvFreeCnt: .res 1 ; Number of bytes in receive buffer
SendHead: .res 1 ; Head of send buffer
SendTail: .res 1 ; Tail of send buffer
SendFreeCnt: .res 1 ; Number of bytes in send buffer
Stopped: .res 1 ; Flow-stopped flag
RtsOff: .res 1
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 $00
SerFlagOrig: .byte $00
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
.byte %11000000 ; SER_BITS_8
TxBitTable: .byte %00000000 ; SER_BITS_5, in WR_TX_CTRL (WR5)
.byte %01000000 ; SER_BITS_6 (Ref page 5-9)
.byte %00100000 ; SER_BITS_7
.byte %01100000 ; SER_BITS_8
.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
; 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)
ParityTable: .byte %00000000 ; SER_PAR_NONE, in WR_TX_RX_CTRL (WR4)
.byte %00000001 ; SER_PAR_ODD (Ref page 5-8)
.byte %00000011 ; SER_PAR_EVEN
.byte $FF ; SER_PAR_MARK
.byte $FF ; SER_PAR_SPACE
; ------------------------------------------------------------------------
; Addresses
SCCAREG := $C039
SCCBREG := $C038
SCCADATA := $C03B
SCCBDATA := $C03A
; We're supposed to get SerFlag's address using GetAddr on ROMs 1 and 3.
; (https://archive.org/details/IIgs_2523018_SCC_Access, page 9)
; But, it's the same value as on ROM0. As we don't expect a ROM 4 anytime
; soon with a different value, let's keep it simple.
SER_FLAG := $E10104
; ------------------------------------------------------------------------
; Channels
CHANNEL_B = 0
CHANNEL_A = 1
; ------------------------------------------------------------------------
; Write registers, read registers, and values that interest us
WR_INIT_CTRL = 0
RR_INIT_STATUS = 0
INIT_CTRL_CLEAR_EIRQ = %00010000
INIT_CTRL_CLEAR_ERR = %00110000
INIT_STATUS_READY = %00000100
INIT_STATUS_RTS = %00100000
WR_TX_RX_MODE_CTRL = 1
TX_RX_MODE_OFF = %00000000
TX_RX_MODE_RXIRQ = %00010001
WR_RX_CTRL = 3 ; (Ref page 5-7)
RR_RX_STATUS = 9 ; Corresponding status register
RX_CTRL_ON = %00000001 ; ORed, Rx enabled
RX_CTRL_OFF = %11111110 ; ANDed,Rx disabled
WR_TX_RX_CTRL = 4
RR_TX_RX_STATUS = 4
WR_TX_CTRL = 5 ; (Ref page 5-9)
RR_TX_STATUS = 5 ; Corresponding status register
TX_CTRL_ON = %00001000 ; ORed, Tx enabled
TX_CTRL_OFF = %11110111 ; ANDed,Tx disabled
TX_DTR_ON = %01111111 ; ANDed,DTR ON (high)
TX_DTR_OFF = %10000000 ; ORed, DTR OFF
TX_RTS_ON = %00000010 ; ORed, RTS ON (low)
TX_RTS_OFF = %11111101 ; ANDed, RTS OFF
WR_MASTER_IRQ_RST = 9 ; (Ref page 5-14)
MASTER_IRQ_SHUTDOWN = %00000010 ; STA'd
MASTER_IRQ_MIE_RST = %00001010 ; STA'd
MASTER_IRQ_SET = %00011001 ; STA'd
WR_CLOCK_CTRL = 11 ; (Ref page 5-17)
WR_BAUDL_CTRL = 12 ; (Ref page 5-18)
WR_BAUDH_CTRL = 13 ; (Ref page 5-19)
WR_MISC_CTRL = 14 ; (Ref page 5-19)
WR_IRQ_CTRL = 15 ; (Ref page 5-20)
IRQ_CLEANUP_EIRQ = %00001000
RR_SPEC_COND_STATUS = 1 ; (Ref page 5-23)
SPEC_COND_FRAMING_ERR = %01000000
SPEC_COND_OVERRUN_ERR = %00100000
RR_IRQ_STATUS = 2 ; (Ref page 5-24)
IRQ_MASQ = %01110000 ; ANDed
IRQ_RX = %00100000
IRQ_SPECIAL = %01100000
RR_INTR_PENDING_STATUS = 3 ; (Ref page 5-25)
INTR_IS_RX = %00100100 ; ANDed (RX IRQ, channel A or B)
.code
; Read register value to A.
; Input: X as channel
; Y as register
; Output: A
readSSCReg:
cpx #0
bne ReadAreg
sty SCCBREG
lda SCCBREG
rts
ReadAreg:
sty SCCAREG
lda SCCAREG
rts
; Write value of A to a register.
; Input: X as channel
; Y as register
writeSCCReg:
cpx #0
bne WriteAreg
sty SCCBREG
sta SCCBREG
rts
WriteAreg:
sty SCCAREG
sta SCCAREG
rts
;----------------------------------------------------------------------------
; SER_INSTALL: Is called after the driver is loaded into memory. If possible,
; check if the hardware is present. Must return an SER_ERR_xx code in a/x.
;
; Since we don't have to manage the IRQ vector on the Apple II, this is
; actually the same as:
;
; SER_UNINSTALL: Is called before the driver is removed from memory.
; No return code required (the driver is removed from memory on return).
;
; and:
;
; SER_CLOSE: Close the port and disable interrupts. Called without parameters.
; Must return an SER_ERR_xx code in a/x.
SER_INSTALL:
SER_UNINSTALL:
SER_CLOSE:
; Check if this is a IIgs (Apple II Miscellaneous TechNote #7,
; Apple II Family Identification)
sec
bit $C082
jsr $FE1F
bit $C080
bcc IIgs
lda #SER_ERR_NO_DEVICE ; Not a IIgs
ldx #>$0000
rts
IIgs:
ldx Opened ; Check for open port
beq :+
ldx Channel
php ; Deactivate interrupts
sei ; if enabled
ldy #WR_MASTER_IRQ_RST
lda #MASTER_IRQ_SHUTDOWN
jsr writeSCCReg
ldy #WR_TX_RX_MODE_CTRL
lda #TX_RX_MODE_OFF
jsr writeSCCReg
; Reset SerFlag to what it was
lda SerFlagOrig
sta SER_FLAG
lda SCCBDATA
; Clear external interrupts (twice)
ldy #WR_INIT_CTRL
lda #INIT_CTRL_CLEAR_EIRQ
jsr writeSCCReg
jsr writeSCCReg
; Reset MIE for firmware use
ldy #WR_MASTER_IRQ_RST
lda #MASTER_IRQ_MIE_RST
jsr writeSCCReg
ldx #$00
stx Opened ; Mark port as closed
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:
php ; Deactivate interrupts
sei ; if enabled
; Check if the handshake setting is valid
ldy #SER_PARAMS::HANDSHAKE ; Handshake
lda (ptr1),y
cmp #SER_HS_SW ; Not supported
beq InvParam
sta HSType ; Store flow control type
; Initialize buffers
ldy #$00
sty Stopped
sty RecvHead
sty RecvTail
sty SendHead
sty SendTail
dey ; Y = 255
sty RecvFreeCnt
sty SendFreeCnt
ldx Channel
ldy #RR_INIT_STATUS ; Hit rr0 once to sync up
jsr readSSCReg
ldy #WR_MISC_CTRL ; WR14: Turn everything off
lda #$00
jsr writeSCCReg
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
pha
ldy #SER_PARAMS::PARITY
lda (ptr1),y ; Parity bits
tay
pla
ora ParityTable,y ; Get value
bmi InvParam
ldy CurClockSource ; Clock multiplier
ora ClockMultiplier,y
ldy #WR_TX_RX_CTRL
jsr writeSCCReg ; End of WR4 setup
ldy CurClockSource ; WR11 setup: clock source
cpx #CHANNEL_B
beq SetClock
iny ; Shift to get correct ClockSource val
iny ; depending on our channel
SetClock:
lda ClockSource,y
ldy #WR_CLOCK_CTRL
jsr writeSCCReg ; End of WR11 setup
lda ChanIrqFlags,x ; Store which IRQ bits we'll check
sta CurChanIrqFlags
SetBaud:
.assert SER_PARAMS::BAUDRATE = 0, error
lda (ptr1) ; Baudrate index - cc65 value
asl
tay
lda BaudTable,y ; Get low byte of register value
bpl BaudOK ; Verify baudrate is supported
InvParam:
lda #SER_ERR_INIT_FAILED
ldy #$00 ; Mark port closed
bra SetupOut
BaudOK:
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
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
jsr writeSCCReg
ldy #SER_PARAMS::DATABITS ; WR3 setup: RX data bits
lda (ptr1),y
tay
lda RxBitTable,y
ora #RX_CTRL_ON ; and turn receiver on
phy
ldy #WR_RX_CTRL
jsr writeSCCReg ; End of WR3 setup
ply
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 ; and turn RTS on
ldy #WR_TX_CTRL
jsr writeSCCReg ; End of WR5 setup
ldy #WR_IRQ_CTRL ; WR15 setup: IRQ
lda #IRQ_CLEANUP_EIRQ
jsr writeSCCReg
ldy #WR_INIT_CTRL ; WR0 setup: clear existing IRQs
lda #INIT_CTRL_CLEAR_EIRQ
jsr writeSCCReg ; Clear (write twice)
jsr writeSCCReg
ldy #WR_TX_RX_MODE_CTRL ; WR1 setup: Activate RX IRQ
lda #TX_RX_MODE_RXIRQ
jsr writeSCCReg
lda SCCBREG ; WR9 setup: Activate master IRQ
ldy #WR_MASTER_IRQ_RST
lda #MASTER_IRQ_SET
jsr writeSCCReg
lda SER_FLAG ; Get SerFlag's current value
sta SerFlagOrig ; and save it
ora ChanIrqMask,x ; Tell firmware which channel IRQs we want
sta SER_FLAG
ldy #$01 ; Mark port opened
lda #SER_ERR_OK
SetupOut:
plp ; Reenable interrupts if needed
ldx #>$0000
sty Opened
rts
;----------------------------------------------------------------------------
; SER_GET: Will fetch a character from the receive buffer and store it into the
; variable pointed to by ptr1. If no data is available, SER_ERR_NO_DATA is
; returned.
SER_GET:
ldx Channel
lda RecvFreeCnt ; Check for buffer empty
cmp #$FF
beq NoData
ldy Stopped ; Check for flow stopped
beq :+
cmp #63 ; Enough free?
bcc :+
stz Stopped ; Release flow control
lda RtsOff
ora #TX_RTS_ON
ldy #WR_TX_CTRL
jsr writeSCCReg
: ldy RecvHead ; Get byte from buffer
lda RecvBuf,y
inc RecvHead
inc RecvFreeCnt
sta (ptr1)
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax
rts
NoData:
lda #SER_ERR_NO_DATA
ldx #>$0000
rts
;----------------------------------------------------------------------------
; SER_PUT: Output character in A.
; Must return an SER_ERR_xx code in a/x.
SER_PUT:
ldx Channel
ldy SendFreeCnt ; Anything to send first?
iny ; Y = $FF?
beq :+
pha
lda #$00 ; TryHard = false
jsr TryToSend
pla
: ldy SendFreeCnt ; Do we have room to store byte?
bne :+
lda #SER_ERR_OVERFLOW
ldx #>$0000
rts
: ldy SendTail ; Put byte into send buffer & send
sta SendBuf,y
inc SendTail
dec SendFreeCnt
lda #$FF ; TryHard = true
jsr TryToSend
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax ; Promote char return value
rts
;----------------------------------------------------------------------------
; SER_STATUS: Return the status in the variable pointed to by ptr1.
; Must return an SER_ERR_xx code in a/x.
; We provide the read register 0, containing interesting info like
; INIT_STATUS_READY (hardware handshake status) or INIT_STATUS_RTS
; (ready to send).
SER_STATUS:
ldx Channel
ldy #RR_INIT_STATUS
jsr readSSCReg
ldx #$00
sta (ptr1)
.assert SER_ERR_OK = 0, error
txa
rts
;----------------------------------------------------------------------------
; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
; specific data in ptr1, and the ioctl code in A.
; Sets communication channel A or B (A = 1, B = 0)
; Must return an SER_ERR_xx code in a/x.
SER_IOCTL:
ora ptr1+1 ; Check data msb and code to be 0
bne :+
ldx ptr1 ; Check data lsb to be 0 or 1
bmi :+
cpx #$02
bcs :+
stx Channel
.assert SER_ERR_OK = 0, error
tax ; Promote char return value
rts
: lda #SER_ERR_INV_IOCTL
ldx #>$0000
rts
;----------------------------------------------------------------------------
; SER_IRQ: Called from the builtin runtime IRQ handler as a subroutine. All
; registers are already saved, no parameters are passed, but the carry flag
; is clear on entry. The routine must return with carry set if the interrupt
; was handled, otherwise with carry clear.
SER_IRQ:
ldy #RR_INTR_PENDING_STATUS ; IRQ status is always in A reg
sty SCCAREG
lda SCCAREG
and CurChanIrqFlags ; Is this ours?
beq Done
and #INTR_IS_RX ; Is this an RX irq?
beq CheckSpecial
ldx Channel
beq ReadBdata
lda SCCADATA
bra ReadDone
ReadBdata:
lda SCCBDATA ; Get byte
ReadDone:
ldx RecvFreeCnt ; Check if we have free space left
beq Flow ; Jump if no space in receive buffer
ldy RecvTail ; Load buffer pointer
sta RecvBuf,y ; Store received byte in buffer
inc RecvTail ; Increment buffer pointer
dec RecvFreeCnt ; Decrement free space counter
cpx #33
bcc Flow ; Assert flow control if buffer space low
rts ; Interrupt handled (carry already set)
CheckSpecial:
; Always check IRQ special flags from Channel B (Ref page 5-24)
ldy #RR_IRQ_STATUS
sty SCCBREG
lda SCCBREG
and #IRQ_MASQ
cmp #IRQ_SPECIAL
beq Special
; Clear exint
ldx Channel
ldy #WR_INIT_CTRL
lda #INIT_CTRL_CLEAR_EIRQ
jsr writeSCCReg
sec
rts
Flow: lda HSType ; Don't touch if no flow control
beq IRQDone
ldx Channel ; Assert flow control if buffer space too low
ldy #WR_TX_CTRL
lda RtsOff
jsr writeSCCReg
sta Stopped
IRQDone:sec ; Interrupt handled
Done: rts
Special:ldx Channel
ldy #RR_SPEC_COND_STATUS
jsr readSSCReg
tax
and #SPEC_COND_FRAMING_ERR
bne BadChar
txa
and #SPEC_COND_OVERRUN_ERR
beq BadChar
ldy #WR_INIT_CTRL
lda #INIT_CTRL_CLEAR_ERR
jsr writeSCCReg
sec
rts
BadChar:
cpx #CHANNEL_B
beq BadCharB
lda SCCADATA
bra BadCharDone
BadCharB:
lda SCCBDATA ; Remove char in error
BadCharDone:
sec
rts
;----------------------------------------------------------------------------
; Try to send a byte. Internal routine. A = TryHard, X = Channel
TryToSend:
sta tmp1 ; Remember tryHard flag
Again: lda SendFreeCnt ; Anything to send?
cmp #$FF
beq Quit ; No
lda Stopped ; Check for flow stopped
bne Quit ; Bail out if it is
Wait:
ldy #RR_INIT_STATUS
jsr readSSCReg ; Check that we're ready to send
tay
and #INIT_STATUS_READY
beq NotReady
tya
and #INIT_STATUS_RTS ; Ready to send
bne Send
NotReady:
bit tmp1 ; Keep trying if must try hard
bmi Wait
Quit: rts
Send: ldy SendHead ; Send byte
lda SendBuf,y
cpx #CHANNEL_B
beq WriteBdata
sta SCCADATA
bra WriteDone
WriteBdata:
sta SCCBDATA
WriteDone:
inc SendHead
inc SendFreeCnt
jmp Again ; Continue flushing TX buffer

View File

@@ -26,6 +26,7 @@
.include "ser-error.inc"
.macpack module
.macpack cpu
; ------------------------------------------------------------------------
; Header. Includes jump table
@@ -37,8 +38,8 @@
.endif
; Driver signature
.byte $73, $65, $72 ; "ser"
.byte SER_API_VERSION ; Serial API version number
.byte $73, $65, $72 ; "ser"
.byte SER_API_VERSION ; Serial API version number
; Library reference
.addr $0000
@@ -57,13 +58,19 @@
;----------------------------------------------------------------------------
; I/O definitions
.if (.cpu .bitand CPU_ISET_65C02)
ACIA := $C088
.else
Offset = $8F ; Move 6502 false read out of I/O to page $BF
ACIA := $C088-Offset
.endif
ACIA = $C088-Offset
ACIA_DATA = ACIA+0 ; Data register
ACIA_STATUS = ACIA+1 ; Status register
ACIA_CMD = ACIA+2 ; Command register
ACIA_CTRL = ACIA+3 ; Control register
ACIA_DATA := ACIA+0 ; Data register
ACIA_STATUS := ACIA+1 ; Status register
ACIA_CMD := ACIA+2 ; Command register
ACIA_CTRL := ACIA+3 ; Control register
SLTROMSEL := $C02D ; For Apple IIgs slot verification
;----------------------------------------------------------------------------
; Global variables
@@ -72,16 +79,18 @@ ACIA_CTRL = ACIA+3 ; Control register
RecvHead: .res 1 ; Head of receive buffer
RecvTail: .res 1 ; Tail of receive buffer
RecvFreeCnt: .res 1 ; Number of bytes in receive buffer
RecvFreeCnt: .res 1 ; Number of free bytes in receive buffer
SendHead: .res 1 ; Head of send buffer
SendTail: .res 1 ; Tail of send buffer
SendFreeCnt: .res 1 ; Number of bytes in send buffer
SendFreeCnt: .res 1 ; Number of free bytes in send buffer
Stopped: .res 1 ; Flow-stopped flag
RtsOff: .res 1 ;
RtsOff: .res 1 ; Cached value of command register with
; flow stopped
HSType: .res 1 ; Flow-control type
RecvBuf: .res 256 ; Receive buffers: 256 bytes
SendBuf: .res 256 ; Send buffers: 256 bytes
RecvBuf: .res 256 ; Receive buffer: 256 bytes
SendBuf: .res 256 ; Send buffer: 256 bytes
Index: .res 1 ; I/O register index
@@ -91,8 +100,9 @@ Slot: .byte $02 ; Default to SSC in slot 2
.rodata
; Tables used to translate RS232 params into register values
BaudTable: ; bit7 = 1 means setting is invalid
BaudTable: ; Table used to translate RS232 baudrate param
; into control register value
; bit7 = 1 means setting is invalid
.byte $FF ; SER_BAUD_45_5
.byte $01 ; SER_BAUD_50
.byte $02 ; SER_BAUD_75
@@ -111,32 +121,44 @@ BaudTable: ; bit7 = 1 means setting is invalid
.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:
BitTable: ; Table used to translate RS232 databits param
; into control register value
.byte $60 ; SER_BITS_5
.byte $40 ; SER_BITS_6
.byte $20 ; SER_BITS_7
.byte $00 ; SER_BITS_8
StopTable:
StopTable: ; Table used to translate RS232 stopbits param
; into control register value
.byte $00 ; SER_STOP_1
.byte $80 ; SER_STOP_2
ParityTable:
ParityTable: ; Table used to translate RS232 parity param
; into command register value
.byte $00 ; SER_PAR_NONE
.byte $20 ; SER_PAR_ODD
.byte $60 ; SER_PAR_EVEN
.byte $A0 ; SER_PAR_MARK
.byte $E0 ; SER_PAR_SPACE
IdOfsTable:
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:
.byte $38 ; Fixed
.byte $18 ; Fixed
.byte $01 ; Fixed
.byte $31 ; Serial or parallel I/O card type 1
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 $31 ; Device signature byte (serial or
; parallel I/O card type 1)
IdTableLen = * - IdValTable
@@ -163,12 +185,10 @@ SER_CLOSE:
ldx Index ; Check for open port
beq :+
; Deactivate DTR and disable 6551 interrupts
lda #%00001010
lda #%00001010 ; Deactivate DTR and disable 6551 interrupts
sta ACIA_CMD,x
; Done, return an error code
: lda #SER_ERR_OK
: lda #SER_ERR_OK ; Done, return an error code
.assert SER_ERR_OK = 0, error
tax
stx Index ; Mark port as closed
@@ -177,104 +197,155 @@ SER_CLOSE:
;----------------------------------------------------------------------------
; SER_OPEN: A pointer to a ser_params structure is passed in ptr1.
; Must return an SER_ERR_xx code in a/x.
; Note: Hardware checks are done in SER_OPEN instead of SER_INSTALL,
; because they depend on the selected slot, and we can't select the slot
; before SER_INSTALL.
SER_OPEN:
ldx #<$C000
; Check if this is a IIgs (Apple II Miscellaneous TechNote #7,
; Apple II Family Identification)
sec
bit $C082
jsr $FE1F
bit $C080
bcs NotIIgs
; We're on a IIgs. For every slot N, either bit N of $C02D is
; 0 for the internal ROM, or 1 for "Your Card". Let's make sure
; that slot N's bit is set to 1, otherwise, that can't be an SSC.
ldy Slot
lda SLTROMSEL
: lsr
dey
bpl :- ; Shift until slot's bit ends in carry
bcc NoDev
NotIIgs:ldx #<$C000
stx ptr2
lda #>$C000
ora Slot
sta ptr2+1
; Check Pascal 1.1 Firmware Protocol ID bytes
: ldy IdOfsTable,x
: ldy IdOfsTable,x ; Check Pascal 1.1 Firmware Protocol ID bytes
lda IdValTable,x
cmp (ptr2),y
bne NoDevice
bne NoDev
inx
cpx #IdTableLen
bcc :-
; Convert slot to I/O register index
lda Slot
lda Slot ; Convert slot to I/O register index
asl
asl
asl
asl
adc #Offset ; Assume carry to be clear
.if .not (.cpu .bitand CPU_ISET_65C02)
adc #Offset ; Assume carry to be clear
.endif
tax
; Check if the handshake setting is valid
ldy #SER_PARAMS::HANDSHAKE ; Handshake
lda (ptr1),y
cmp #SER_HS_HW ; This is all we support
bne InvParam
; Check that this works like an ACIA 6551 is expected to work
; Initialize buffers
ldy #$00
lda ACIA_STATUS,x ; Save current values in what we expect to be
sta tmp1 ; the ACIA status register
lda ACIA_CMD,x ; and command register. So we can restore them
sta tmp2 ; if this isn't a 6551.
ldy #%00000010 ; Disable TX/RX, disable IRQ
: tya
sta ACIA_CMD,x
cmp ACIA_CMD,x ; Verify what we stored is there
bne NotAcia
iny ; Enable TX/RX, disable IRQ
cpy #%00000100
bne :-
sta ACIA_STATUS,x ; Reset ACIA
lda ACIA_CMD,x ; Check that RX/TX is disabled
lsr
bcc AciaOK
NotAcia:lda tmp2 ; Restore original values
sta ACIA_CMD,x
lda tmp1
sta ACIA_STATUS,x
NoDev: lda #SER_ERR_NO_DEVICE
bne Out
; Check if the handshake setting is valid
AciaOK: ldy #SER_PARAMS::HANDSHAKE
lda (ptr1),y
cmp #SER_HS_SW ; Not supported
bne HandshakeOK
lda #SER_ERR_INIT_FAILED
bne Out
HandshakeOK:
sta HSType ; Store flow control type
ldy #$00 ; Initialize buffers
sty Stopped
sty RecvHead
sty RecvTail
sty SendHead
sty SendTail
dey ; Y = 255
dey ; Y = 255
sty RecvFreeCnt
sty SendFreeCnt
; Set the value for the control register, which contains stop bits,
; word length and the baud rate.
ldy #SER_PARAMS::BAUDRATE
lda (ptr1),y ; Baudrate index
lda (ptr1),y ; Baudrate index
tay
lda BaudTable,y ; Get 6551 value
bmi InvBaud ; Branch if rate not supported
sta tmp1
lda BaudTable,y ; Get 6551 value
sta tmp2 ; Backup for IRQ setting
bpl BaudOK ; Check that baudrate is supported
ldy #SER_PARAMS::DATABITS ; Databits
lda (ptr1),y
lda #SER_ERR_BAUD_UNAVAIL
bne Out
BaudOK: sta tmp1
ldy #SER_PARAMS::DATABITS
lda (ptr1),y ; Databits index
tay
lda BitTable,y
lda BitTable,y ; Get 6551 value
ora tmp1
sta tmp1
ldy #SER_PARAMS::STOPBITS ; Stopbits
lda (ptr1),y
ldy #SER_PARAMS::STOPBITS
lda (ptr1),y ; Stopbits index
tay
lda StopTable,y
lda StopTable,y ; Get 6551 value
ora tmp1
ora #%00010000 ; Receiver clock source = baudrate
ora #%00010000 ; Set receiver clock source = baudrate
sta ACIA_CTRL,x
; Set the value for the command register. We remember the base value
; in RtsOff, since we will have to manipulate ACIA_CMD often.
ldy #SER_PARAMS::PARITY ; Parity
lda (ptr1),y
ldy #SER_PARAMS::PARITY
lda (ptr1),y ; Parity index
tay
lda ParityTable,y
ora #%00000001 ; DTR active
sta RtsOff
ora #%00001000 ; Enable receive interrupts
sta ACIA_CMD,x
lda ParityTable,y ; Get 6551 value
ora #%00000001 ; Set DTR active
sta RtsOff ; Store value to easily handle flow control later
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
stx Index ; Mark port as open
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax
rts
; Device (hardware) not found
NoDevice:lda #SER_ERR_NO_DEVICE
ldx #0 ; return value is char
rts
; Invalid parameter
InvParam:lda #SER_ERR_INIT_FAILED
ldx #0 ; return value is char
rts
; Baud rate not available
InvBaud:lda #SER_ERR_BAUD_UNAVAIL
ldx #0 ; return value is char
Out:
ldx #>$0000
rts
;----------------------------------------------------------------------------
@@ -284,38 +355,38 @@ InvBaud:lda #SER_ERR_BAUD_UNAVAIL
SER_GET:
ldx Index
ldy SendFreeCnt ; Send data if necessary
iny ; Y == $FF?
beq :+
lda #$00 ; TryHard = false
jsr TryToSend
; Check for buffer empty
: lda RecvFreeCnt ; (25)
lda RecvFreeCnt ; Check for buffer empty
cmp #$FF
bne :+
lda #SER_ERR_NO_DATA
ldx #0 ; return value is char
ldx #>$0000
rts
; Check for flow stopped & enough free: release flow control
: ldy Stopped ; (34)
: ldy Stopped ; Check for flow stopped
beq :+
cmp #63
cmp #63 ; Enough free?
bcc :+
.if (.cpu .bitand CPU_ISET_65C02)
stz Stopped ; Release flow control
.else
lda #$00
sta Stopped
.endif
lda RtsOff
ora #%00001000
sta ACIA_CMD,x
; Get byte from buffer
: ldy RecvHead ; (41)
: ldy RecvHead ; Get byte from buffer
lda RecvBuf,y
inc RecvHead
inc RecvFreeCnt
ldx #$00 ; (59)
ldx #$00
.if (.cpu .bitand CPU_ISET_65C02)
sta (ptr1) ; Store it for caller
.else
sta (ptr1,x)
.endif
txa ; Return code = 0
rts
@@ -326,28 +397,26 @@ SER_GET:
SER_PUT:
ldx Index
; Try to send
ldy SendFreeCnt
iny ; Y = $FF?
ldy SendFreeCnt ; Anything to send first?
cpy #$FF ; No
beq :+
pha
lda #$00 ; TryHard = false
jsr TryToSend
jsr TryToSend ; Try to flush send buffer
pla
; Put byte into send buffer & send
: ldy SendFreeCnt
ldy SendFreeCnt ; Reload SendFreeCnt after TryToSend
bne :+
lda #SER_ERR_OVERFLOW
ldx #0 ; return value is char
ldx #>$0000
rts
: ldy SendTail
: ldy SendTail ; Put byte into send buffer
sta SendBuf,y
inc SendTail
dec SendFreeCnt
lda #$FF ; TryHard = true
jsr TryToSend
jsr TryToSend ; Flush send buffer
lda #SER_ERR_OK
.assert SER_ERR_OK = 0, error
tax
@@ -369,26 +438,25 @@ SER_STATUS:
;----------------------------------------------------------------------------
; SER_IOCTL: Driver defined entry point. The wrapper will pass a pointer to ioctl
; specific data in ptr1, and the ioctl code in A.
; The ioctl data is the slot number to open.
; Must return an SER_ERR_xx code in a/x.
SER_IOCTL:
; Check data msb and code to be 0
ora ptr1+1
ora ptr1+1 ; Check data msb and code to be 0
bne :+
; Check data lsb to be [1..7]
ldx ptr1
ldx ptr1 ; Check data lsb to be [1..7]
beq :+
cpx #7+1
bcs :+
stx Slot
stx Slot ; Store slot
.assert SER_ERR_OK = 0, error
tax
rts
: lda #SER_ERR_INV_IOCTL
ldx #0 ; return value is char
ldx #>$0000
rts
;----------------------------------------------------------------------------
@@ -404,22 +472,24 @@ SER_IRQ:
and #$08
beq Done ; Jump if no ACIA interrupt
lda ACIA_DATA,x ; Get byte from ACIA
ldy RecvFreeCnt ; Check if we have free space left
ldx RecvFreeCnt ; Check if we have free space left
beq Flow ; Jump if no space in receive buffer
ldy RecvTail ; Load buffer pointer
sta RecvBuf,y ; Store received byte in buffer
inc RecvTail ; Increment buffer pointer
dec RecvFreeCnt ; Decrement free space counter
ldy RecvFreeCnt ; Check for buffer space low
cpy #33
cpx #33 ; Check for buffer space low
bcc Flow ; Assert flow control if buffer space low
rts ; Interrupt handled (carry already set)
; Assert flow control if buffer space too low
Flow: lda RtsOff
Flow: lda HSType ; Don't touch if no flow control
beq IRQDone
ldx Index ; Assert flow control if buffer space too low
lda RtsOff
sta ACIA_CMD,x
sta Stopped
sec ; Interrupt handled
IRQDone:sec ; Interrupt handled
Done: rts
;----------------------------------------------------------------------------
@@ -427,26 +497,24 @@ Done: rts
TryToSend:
sta tmp1 ; Remember tryHard flag
Again: lda SendFreeCnt
cmp #$FF
beq Quit ; Bail out
NextByte:
lda SendFreeCnt ; Is there anything to send? This can happen if
cmp #$FF ; we got interrupted by RX while sending, and
beq Quit ; flow control was asserted.
; Check for flow stopped
lda Stopped
bne Quit ; Bail out
Again: lda Stopped ; Is flow stopped?
bne Quit ; Yes, Bail out
; Check that ACIA is ready to send
lda ACIA_STATUS,x
lda ACIA_STATUS,x ; Check that ACIA is ready to send
and #$10
bne Send
bne Send ; It is!
bit tmp1 ; Keep trying if must try hard
bmi Again
Quit: rts
; Send byte and try again
Send: ldy SendHead
Send: ldy SendHead ; Get first byte to send
lda SendBuf,y
sta ACIA_DATA,x
sta ACIA_DATA,x ; Send it
inc SendHead
inc SendFreeCnt
jmp Again
bne NextByte ; And try next one

View File

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

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

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

129
libsrc/apple2/stat.s Normal file
View File

@@ -0,0 +1,129 @@
;
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
; 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

123
libsrc/apple2/statvfs.s Normal file
View File

@@ -0,0 +1,123 @@
;
; Colin Leroy-Mira, 2023 <colin@colino.net>
;
; 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

View File

@@ -29,7 +29,8 @@ __syschdir:
bcs cleanup
; Update current working directory
jsr initcwd ; Returns with A = 0
jsr initcwd
lda #$00
; Cleanup name
cleanup:jsr popname ; Preserves A

View File

@@ -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) {

View File

@@ -342,7 +342,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

View File

@@ -321,7 +321,7 @@ GETPIXEL:
jsr SCRN
tax
lda COL2TGI,x
ldx #$00
ldx #>$0000
bit $C080 ; Switch in LC bank 2 for R/O
rts

View File

@@ -0,0 +1,9 @@
;
; Oliver Schmidt, 2024-08-06
;
.export uppercasemask
.data
uppercasemask: .byte $DF ; Convert to uppercase

View File

@@ -6,7 +6,6 @@
.ifdef __APPLE2ENH__
.export _videomode
.import COUT
.include "apple2.inc"
@@ -17,52 +16,25 @@ _videomode:
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 #$15 ; Ctrl-char code for 40 cols
plp
bpl :+
inc a ; Ctrl-char code for 80 cols
lda #$00 ; Ctrl-char code for 80 cols
: rts ; X was preserved all the way
.endif ; __APPLE2ENH__

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

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

View File

@@ -5,21 +5,11 @@
;
.ifdef __APPLE2ENH__
.constructor initvsync
.export _waitvsync
.import _get_ostype
.import ostype
.include "apple2.inc"
.segment "ONCE"
initvsync:
jsr _get_ostype
sta ostype
rts
.code
_waitvsync:
bit ostype
bmi iigs ; $8x
@@ -39,7 +29,8 @@ iigs: bit RDVBLBAR
rts
; Apple IIc TechNote #9, Detecting VBL
iic: sei
iic: php
sei
sta IOUDISOFF
lda RDVBLMSK
bit ENVBL
@@ -50,11 +41,7 @@ iic: sei
bcs :+ ; VBL interrupts were already enabled
bit DISVBL
: sta IOUDISON ; IIc Tech Ref Man: The firmware normally leaves IOUDIS on.
cli
plp
rts
.segment "INIT"
ostype: .res 1
.endif ; __APPLE2ENH__

View File

@@ -10,5 +10,10 @@
_wherex:
lda CH
ldx #$00
.ifdef __APPLE2ENH__
bit RD80VID ; In 80 column mode?
bpl :+
lda OURCH
: .endif
ldx #>$0000
rts

View File

@@ -12,5 +12,5 @@ _wherey:
lda CV
sec
sbc WNDTOP
ldx #$00
ldx #>$0000
rts

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

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

View File

@@ -33,7 +33,7 @@ done: lda ICBLL,x ; buf len lo
lda ICBLH,x ; get buf len hi
tax ; to X
okdone: lda #0
sta ___oserror ; clear system dependend error code
sta ___oserror ; clear system dependent error code
pla ; get buf len lo
rts
@@ -147,6 +147,7 @@ icbll_copy:
sta dataptr+1
lda ICBLL,x
sta copylen
beq copied ; length = 0 if EOF
pha ; remember for return value
ldy #0
ldx index
@@ -159,7 +160,7 @@ copy: lda linebuf,x
bne copy
pla ; length
pha ; save length to return at okdone
copied: pha ; save length to return at okdone
clc
adc index

View File

@@ -23,7 +23,7 @@
.proc __sio_call
sta DCOMND ; set command into DCB
stx DSTATS ; set data flow directon
stx DSTATS ; set data flow direction
jsr popax ; get buffer address
sta DBUFLO ; set buffer address into DCB
stx DBUFHI

View File

@@ -21,7 +21,7 @@ write9:
lda ICBLH,x ; buf len high
tax
lda #0
sta ___oserror ; clear system dependend error code
sta ___oserror ; clear system dependent error code
pla
rts

View File

@@ -7,8 +7,9 @@
.export _clrscr
.include "atari5200.inc"
.importzp ptr1
.importzp screen_width, screen_height
SCRSIZE = 480 ; 20x24: size of default conio atari5200 screen
SCRSIZE = screen_width * screen_height
_clrscr:lda SAVMSC ; screen memory
sta ptr1

View File

@@ -7,6 +7,7 @@
SCREEN_BUF_SIZE = 20 * 24
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
sta SDLSTL
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

View File

@@ -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
sta SDLSTL
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

View File

@@ -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

View File

@@ -227,14 +227,8 @@ InvBaud:lda #<SER_ERR_BAUD_UNAVAIL
; returned.
SER_GET:
ldy SendFreeCnt ; Send data if necessary
iny ; Y == $FF?
beq :+
lda #$00 ; TryHard = false
jsr TryToSend
; Check for buffer empty
: lda RecvFreeCnt ; (25)
lda RecvFreeCnt ; (25)
cmp #$FF
bne :+
lda #SER_ERR_NO_DATA
@@ -269,20 +263,21 @@ SER_GET:
SER_PUT:
; Try to send
ldy SendFreeCnt
iny ; Y = $FF?
cpy #$FF ; Nothing to flush
beq :+
pha
lda #$00 ; TryHard = false
jsr TryToSend
pla
; Put byte into send buffer & send
: ldy SendFreeCnt
; Reload SendFreeCnt after TryToSend
ldy SendFreeCnt
bne :+
lda #SER_ERR_OVERFLOW
ldx #0 ; return value is char
rts
; Put byte into send buffer & send
: ldy SendTail
sta SendBuf,y
inc SendTail
@@ -329,19 +324,19 @@ SER_IRQ:
and #$08
beq Done ; Jump if no ACIA interrupt
lda ACIA::DATA,x ; Get byte from ACIA
ldy RecvFreeCnt ; Check if we have free space left
ldx RecvFreeCnt ; Check if we have free space left
beq Flow ; Jump if no space in receive buffer
ldy RecvTail ; Load buffer pointer
sta RecvBuf,y ; Store received byte in buffer
inc RecvTail ; Increment buffer pointer
dec RecvFreeCnt ; Decrement free space counter
ldy RecvFreeCnt ; Check for buffer space low
cpy #33
cpx #33
bcc Flow ; Assert flow control if buffer space low
rts ; Interrupt handled (carry already set)
; Assert flow control if buffer space too low
Flow: lda RtsOff
Flow: ldx Index ; Reload port
lda RtsOff
sta ACIA::CMD,x
sta Stopped
sec ; Interrupt handled
@@ -352,12 +347,13 @@ Done: rts
TryToSend:
sta tmp1 ; Remember tryHard flag
Again: lda SendFreeCnt
NextByte:
lda SendFreeCnt
cmp #$FF
beq Quit ; Bail out
; Check for flow stopped
lda Stopped
Again: lda Stopped
bne Quit ; Bail out
; Check that ACIA is ready to send
@@ -374,4 +370,4 @@ Send: ldy SendHead
sta ACIA::DATA
inc SendHead
inc SendFreeCnt
jmp Again
jmp NextByte

18
libsrc/atmos/waitvsync.s Normal file
View File

@@ -0,0 +1,18 @@
;
; Written by Stefan Haubenthal <polluks@sdf.org>, requires VSync hack
;
; void waitvsync (void);
;
.export _waitvsync
.include "atmos.inc"
.proc _waitvsync
lda #%00010000 ; CB1
wait: and VIA::PRA2
bne wait
rts
.endproc

View File

@@ -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

View File

@@ -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
@@ -314,15 +314,10 @@ SER_CLOSE:
;
SER_GET:
ldx SendFreeCnt ; Send data if necessary
inx ; X == $FF?
beq @L1
lda #$00
jsr TryToSend
; Check for buffer empty
@L1: lda RecvFreeCnt ; (25)
lda RecvFreeCnt ; (25)
cmp #$ff
bne @L2
lda #SER_ERR_NO_DATA
@@ -362,21 +357,23 @@ SER_PUT:
; Try to send
ldx SendFreeCnt
inx ; X = $ff?
cpx #$FF ; Nothing to flush
beq @L2
pha
lda #$00
jsr TryToSend
pla
; Put byte into send buffer & send
; Reload SendFreeCnt after TryToSend
@L2: ldx SendFreeCnt
bne @L3
ldx SendFreeCnt
bne @L2
lda #SER_ERR_OVERFLOW ; X is already zero
rts
@L3: ldx SendTail
; Put byte into send buffer & send
@L2: ldx SendTail
sta SendBuf,x
inc SendTail
dec SendFreeCnt
@@ -466,25 +463,25 @@ NmiHandler:
sta tmp1 ; Remember tryHard flag
@L0: lda SendFreeCnt
cmp #$ff
beq @L3 ; Bail out
beq @L2 ; Bail out
; Check for flow stopped
@L1: lda Stopped
bne @L3 ; Bail out
bne @L2 ; Bail out
; Check that swiftlink is ready to send
@L2: lda ACIA_STATUS
lda ACIA_STATUS
and #$10
bne @L4
bne @L3
bit tmp1 ;keep trying if must try hard
bmi @L0
@L3: rts
bmi @L1
@L2: rts
; Send byte and try again
@L4: ldx SendHead
@L3: ldx SendHead
lda SendBuf,x
sta ACIA_DATA
inc SendHead

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -212,8 +212,8 @@ MAP:
cmp pagecount
bcs return_null
sta curpage
lda #<dummy ; load .A/.X with adress of data for COPYFROM-call (which expects the
ldx #>dummy ; adress in .A/.X)
lda #<dummy ; load .A/.X with address of data for COPYFROM-call (which expects the
ldx #>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 ; load .A/.X with adress of data for COPYTO-call (which expects the
ldx #>dummy ; adress in .A/.X)
lda #<dummy ; load .A/.X with address of data for COPYTO-call (which expects the
ldx #>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

View File

@@ -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

View File

@@ -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
@@ -288,15 +288,10 @@ SER_CLOSE:
;
SER_GET:
ldx SendFreeCnt ; Send data if necessary
inx ; X == $FF?
beq @L1
lda #$00
jsr TryToSend
; Check for buffer empty
@L1: lda RecvFreeCnt ; (25)
lda RecvFreeCnt ; (25)
cmp #$ff
bne @L2
lda #SER_ERR_NO_DATA
@@ -336,21 +331,23 @@ SER_PUT:
; Try to send
ldx SendFreeCnt
inx ; X = $ff?
cpx #$FF ; Nothing to flush
beq @L2
pha
lda #$00
jsr TryToSend
pla
; Put byte into send buffer & send
; Reload SendFreeCnt after TryToSend
@L2: ldx SendFreeCnt
bne @L3
ldx SendFreeCnt
bne @L2
lda #SER_ERR_OVERFLOW ; X is already zero
rts
@L3: ldx SendTail
; Put byte into send buffer & send
@L2: ldx SendTail
sta SendBuf,x
inc SendTail
dec SendFreeCnt
@@ -443,25 +440,25 @@ NmiHandler:
sta tmp1 ; Remember tryHard flag
@L0: lda SendFreeCnt
cmp #$ff
beq @L3 ; Bail out
beq @L2 ; Bail out
; Check for flow stopped
@L1: lda Stopped
bne @L3 ; Bail out
bne @L2 ; Bail out
; Check that swiftlink is ready to send
@L2: lda ACIA_STATUS
lda ACIA_STATUS
and #$10
bne @L4
bne @L3
bit tmp1 ;keep trying if must try hard
bmi @L0
@L3: rts
bmi @L1
@L2: rts
; Send byte and try again
@L4: ldx SendHead
@L3: ldx SendHead
lda SendBuf,x
sta ACIA_DATA
inc SendHead

View File

@@ -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

View File

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

View File

@@ -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);

View File

@@ -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 ;-)

View File

@@ -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.
;

View File

@@ -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

View File

@@ -244,15 +244,10 @@ InvBaud:
;
SER_GET:
ldx SendFreeCnt ; Send data if necessary
inx ; X == $FF?
beq @L1
lda #$00
jsr TryToSend
; Check for buffer empty
@L1: lda RecvFreeCnt
lda RecvFreeCnt
cmp #$ff
bne @L2
lda #SER_ERR_NO_DATA
@@ -292,21 +287,23 @@ SER_PUT:
; Try to send
ldx SendFreeCnt
inx ; X = $ff?
cpx #$FF ; Nothing to flush
beq @L2
pha
lda #$00
jsr TryToSend
pla
; Put byte into send buffer & send
; Reload SendFreeCnt after TryToSend
@L2: ldx SendFreeCnt
bne @L3
ldx SendFreeCnt
bne @L2
lda #SER_ERR_OVERFLOW ; X is already zero
rts
@L3: ldx SendTail
; Put byte into send buffer & send
@L2: ldx SendTail
sta SendBuf,x
inc SendTail
dec SendFreeCnt
@@ -395,31 +392,31 @@ SER_IRQ:
sta IndReg ; Switch to the system bank
@L0: lda SendFreeCnt
cmp #$ff
beq @L3 ; Bail out
beq @L2 ; Bail out
; Check for flow stopped
@L1: lda Stopped
bne @L3 ; Bail out
bne @L2 ; Bail out
; Check that swiftlink is ready to send
@L2: ldy #ACIA::STATUS
ldy #ACIA::STATUS
lda (acia),y
and #$10
bne @L4
bne @L3
bit tmp1 ; Keep trying if must try hard
bmi @L0
bmi @L1
; Switch back the bank and return
@L3: lda ExecReg
@L2: lda ExecReg
sta IndReg
rts
; Send byte and try again
@L4: ldx SendHead
@L3: ldx SendHead
lda SendBuf,x
ldy #ACIA::DATA
sta (acia),y

View File

@@ -245,15 +245,10 @@ InvBaud:
;
SER_GET:
ldx SendFreeCnt ; Send data if necessary
inx ; X == $FF?
beq @L1
lda #$00
jsr TryToSend
; Check for buffer empty
@L1: lda RecvFreeCnt
lda RecvFreeCnt
cmp #$ff
bne @L2
lda #SER_ERR_NO_DATA
@@ -293,21 +288,23 @@ SER_PUT:
; Try to send
ldx SendFreeCnt
inx ; X = $ff?
cpx #$ff ; Nothing to flush
beq @L2
pha
lda #$00
jsr TryToSend
pla
; Put byte into send buffer & send
; Reload SendFreeCnt after TryToSend
@L2: ldx SendFreeCnt
bne @L3
ldx SendFreeCnt
bne @L2
lda #SER_ERR_OVERFLOW ; X is already zero
rts
@L3: ldx SendTail
; Put byte into send buffer & send
@L2: ldx SendTail
sta SendBuf,x
inc SendTail
dec SendFreeCnt
@@ -395,31 +392,31 @@ SER_IRQ:
sta IndReg ; Switch to the system bank
@L0: lda SendFreeCnt
cmp #$ff
beq @L3 ; Bail out
beq @L2 ; Bail out
; Check for flow stopped
@L1: lda Stopped
bne @L3 ; Bail out
bne @L2 ; Bail out
; Check that swiftlink is ready to send
@L2: ldy #ACIA::STATUS
ldy #ACIA::STATUS
lda (acia),y
and #$10
bne @L4
bne @L3
bit tmp1 ; Keep trying if must try hard
bmi @L0
bmi @L1
; Switch back the bank and return
@L3: lda ExecReg
@L2: lda ExecReg
sta IndReg
rts
; Send byte and try again
@L4: ldx SendHead
@L3: ldx SendHead
lda SendBuf,x
ldy #ACIA::DATA
sta (acia),y

View File

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

View File

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

View File

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

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

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

View File

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

View File

@@ -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"

View File

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

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