Added old lib and two examples

This commit is contained in:
Mattias Hansson 2025-11-21 12:01:54 +01:00
parent 040c03467b
commit 312f724971
21 changed files with 4615 additions and 0 deletions

20
examples/hires/cm.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/sh
# Define filename as variable
PROGNAME="hires"
# Only set C65LIBPATH if not already defined
if [ -z "$C65LIBPATH" ]; then
export C65LIBPATH=$(readlink -f "../../lib")
fi
# Compile
c65gm -in ${PROGNAME}.c65 -out ${PROGNAME}.s
if [ $? -ne 0 ]; then
echo "Compilation terminated"
exit 1
fi
echo assemble.
acme ${PROGNAME}.s
if [ -f ${PROGNAME}.prg ]; then
rm ${PROGNAME}.prg
fi
# main.bin ${PROGNAME}.prg
mv main.bin main.prg

57
examples/hires/hires.c65 Normal file
View file

@ -0,0 +1,57 @@
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>
GOTO start
FUNC sethires
BYTE b
b = PEEK $d011
b = b | 32 //enable bitmap mode
POKE $d011 , b
b = PEEK $d018
b = b & %11110000
b = b | 8 //enable bitmap mode
POKE $d018 , b
FEND
FUNC fillmem({WORD start_addr @ $fa} {WORD end_addr @ $fc} {BYTE value})
WHILE start_addr <= end_addr
POKE start_addr , value
start_addr++
WEND
FEND
FUNC main
sethires()
WORD CONST screen = $0400
fillmem(screen, screen+1000, $cf)
WHILE 1
fillmem($2000, $3fff, %00000001)
fillmem($2000, $3fff, %00000010)
fillmem($2000, $3fff, %00000100)
fillmem($2000, $3fff, %00001000)
fillmem($2000, $3fff, %00010000)
fillmem($2000, $3fff, %00100000)
fillmem($2000, $3fff, %01000000)
fillmem($2000, $3fff, %10000000)
WEND
FEND
LABEL start
main()

View file

@ -0,0 +1 @@
x64 -autostartprgmode 1 main.prg

20
examples/multicolorbm/cm.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/sh
# Define filename as variable
PROGNAME="multicolorbm"
# Only set C65LIBPATH if not already defined
if [ -z "$C65LIBPATH" ]; then
export C65LIBPATH=$(readlink -f "../../lib")
fi
# Compile
c65gm -in ${PROGNAME}.c65 -out ${PROGNAME}.s
if [ $? -ne 0 ]; then
echo "Compilation terminated"
exit 1
fi
echo assemble.
acme ${PROGNAME}.s
if [ -f ${PROGNAME}.prg ]; then
rm ${PROGNAME}.prg
fi
# main.bin ${PROGNAME}.prg
mv main.bin main.prg

View file

@ -0,0 +1,58 @@
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>
GOTO start
FUNC setmulti
BYTE b
b = PEEK $d011
b = b | 32
POKE $d011 , b
b = PEEK $d016
b = b | 16
POKE $d016 , b
b = PEEK $d018
b = b & %11110000
b = b | 8
POKE $d018 , b
FEND
FUNC fillmem({WORD start_addr @ $fa} {WORD end_addr @ $fc} {BYTE value})
WHILE start_addr <= end_addr
POKE start_addr , value
start_addr++
WEND
FEND
FUNC main
setmulti()
WORD CONST screen = $0400
fillmem(screen, screen+999, $12)
fillmem(colorram, colorram+999, $03)
POKE $d021 , 0
WHILE 1
fillmem($2000, $3fff, %00011011)
fillmem($2000, $3fff, %01101100)
fillmem($2000, $3fff, %10110001)
fillmem($2000, $3fff, %11000110)
WEND
FEND
LABEL start
main()

View file

@ -0,0 +1 @@
x64 -autostartprgmode 1 main.prg

40
lib/c64defs.c65 Normal file
View file

@ -0,0 +1,40 @@
//-----------------------------------------------------------
//
// Commodore 64 definitions library
//
//
// Author: Mattias Hansson, Mikael Hansson
// Copyright (c) : 2000-2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies:
// Target: Commodore 64
//
// Purpose: Define accessor constants to various resources
// in the C64.
//-----------------------------------------------------------
#IFNDEF __C64DEFS
#DEFINE __C64DEFS = 1
WORD CONST vic2 = $d000
WORD CONST cia1 = $dc00
WORD CONST cia2 = $dd00
WORD CONST colorram = $d800
WORD CONST sid = $d400
BYTE CONST color_black = 0
BYTE CONST color_white = 1
BYTE CONST color_red = 2
BYTE CONST color_cyan = 3
BYTE CONST color_purple = 4
BYTE CONST color_green = 5
BYTE CONST color_blue = 6
BYTE CONST color_yellow = 7
BYTE CONST color_orange = 8
BYTE CONST color_brown = 9
BYTE CONST color_pink = 10
BYTE CONST color_dark_grey = 11
BYTE CONST color_grey = 12
BYTE CONST color_light_green = 13
BYTE CONST color_light_blue = 14
BYTE CONST color_light_grey = 15
#IFEND

52
lib/c64kernal.c65 Normal file
View file

@ -0,0 +1,52 @@
//-----------------------------------------------------------
//
// Commodore 64 kernal definitions
//
//
// Author: Mattias Hansson
// Copyright (c) : 2025 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.6+
// Dependencies:
// Target: Commodore 64
//
// Purpose: Define accessor constants to various kernal
// routines in the C64.
//-----------------------------------------------------------
#IFNDEF __C64KERNAL
#DEFINE __C64KERNAL = 1
WORD CONST CINT = $FF81
WORD CONST IOINIT = $FF84
WORD CONST RAMTAS = $FF87
WORD CONST RESTOR = $FF8A
WORD CONST VECTOR = $FF8D
WORD CONST SETMSG = $FF90
WORD CONST SECOND = $FF93
WORD CONST TKSA = $FF96
WORD CONST MEMTOP = $FF99
WORD CONST MEMBOT = $FF9C
WORD CONST SCNKEY = $FF9F
WORD CONST SETTMO = $FFA2
WORD CONST ACPTR = $FFA5
WORD CONST CIOUT = $FFA8
WORD CONST UNTLK = $FFAB
WORD CONST UNLSN = $FFAE
WORD CONST LISTEN = $FFB1
WORD CONST TALK = $FFB4
WORD CONST READST = $FFB7
WORD CONST SETLFS = $FFBA
WORD CONST SETNAM = $FFBD
WORD CONST OPEN = $FFC0
WORD CONST CLOSE = $FFC3
WORD CONST LOAD = $FFD5
WORD CONST SAVE = $FFD8
WORD CONST SETTIM = $FFDB
WORD CONST RDTIM = $FFDE
WORD CONST CLRSCR = $E544 // Clear the screen
WORD CONST KBDREAD = $E5B4 // Get Character From Keyboard Buffer
WORD CONST NMIEXIT = $FEBC
WORD CONST UPDCRAMPTR = $EA24 // Update color ram pointer
#IFEND

25
lib/c64scr.c65 Normal file
View file

@ -0,0 +1,25 @@
#IFNDEF __C64_SCR
#DEFINE __C64_SCR = 1
GOTO lib_c64scr_skip
LABEL lib_c64scr_blank
ASM
lda $d011
and #($ff-$10)
sta $d011
ENDASM
SUBEND
LABEL lib_c64scr_show
ASM
lda $d011
ora #$10
sta $d011
ENDASM
SUBEND
LABEL lib_c64scr_skip
#IFEND

34
lib/c64start.c65 Normal file
View file

@ -0,0 +1,34 @@
//-------------------------------------------------------
//
// C64 START LIB
//
// Author: Mattias Hansson
// License: LGPL
// Compiler version: c65cm, v.04+
//
// Purpose:
// When loaded into CBM memory this source generates
// a valid basic line that starts the program.
//
// Note: Include this first before any own written code,
// and other libraries.
//-------------------------------------------------------
ORIGIN $0801
#IFNDEF __C64_BASIC_START
#DEFINE __C64_BASIC_START = 1
#DEFINE MACHINE_C64 = 1
ASM
!to "main.bin", cbm ; The output file definition (add CBM starting address)
!sl "main.sym" ; save the symbols to separate file
!cpu 6502 ; The processor definition
; basic line "0 sys 2064"
!8 $0c, $08, $00, $00, $9e, $20, $32, $30, $36, $34, $0, $0, $0
ENDASM
ORIGIN $0810
#IFEND

136
lib/cbmiolib.c65 Normal file
View file

@ -0,0 +1,136 @@
//------------------------------------------------------------------------
// Library CBM I/O
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies: (all libs might be included, selectively)
// Target: CBM computers.
//
// Purpose: To shorten lib names to normal usage length
//------------------------------------------------------------------------
#IFNDEF __LIB_CBMIO
#DEFINE __LIB_CBMIO = 1
GOTO lib_cbmio_skip
WORD lib_cbmio_to_txtptr
FUNC lib_cbmio_print ( lib_cbmio_to_txtptr )
//On c64 we "borrow" the undocumented "print zero-terminated string" routine
#IFDEF MACHINE_C64
ASM
lda lib_cbmio_to_txtptr
ldy lib_cbmio_to_txtptr+1
jsr $ab1e ; strout kernal routine
ENDASM
#IFEND
#IFNDEF MACHINE_C64
BYTE lib_cbmio_to_char
PEEK lib_cbmio_to_txtptr -> lib_cbmio_to_char
WHILE lib_cbmio_to_char
GOSUB $ffd2 PASSING lib_cbmio_to_char AS ACC
INC lib_cbmio_to_txtptr
PEEK lib_cbmio_to_txtptr -> lib_cbmio_to_char
WEND
#IFEND
FEND
WORD lib_cbmio_tl_txtptr
FUNC lib_cbmio_printlf ( lib_cbmio_tl_txtptr )
CALL lib_cbmio_print ( lib_cbmio_tl_txtptr )
CALL lib_cbmio_print ( @lib_cbmio_tl_linefeed )
FEND
FUNC lib_cbmio_lf
CALL lib_cbmio_print ( @lib_cbmio_tl_linefeed )
FEND
FUNC lib_cbmio_home
CALL lib_cbmio_print ( @lib_cbmio_tl_home )
FEND
ASM
lib_cbmio_tl_home
!8 19, 0
lib_cbmio_tl_linefeed
!8 13, 0
lib_cbmio_text_nullstr
!8 0
ENDASM
BYTE lib_cbmio_hb_value
FUNC lib_cbmio_hexoutb ( lib_cbmio_hb_value )
ASM
;ldy #0
;lda (lib_cbmio_ho_varaddress),y
lda lib_cbmio_hb_value
and #$0f
tax
lda lib_cbmio_ho_hexdigits,x
sta lib_cbmio_ho_hextxt+1
;lda (lib_cbmio_ho_varaddress),y
lda lib_cbmio_hb_value
lsr
lsr
lsr
lsr
tax
lda lib_cbmio_ho_hexdigits,x
sta lib_cbmio_ho_hextxt
ENDASM
CALL lib_cbmio_print ( @lib_cbmio_ho_hextxt )
FEND
WORD lib_cbmio_lib_cbmio_ho_value
FUNC lib_cbmio_hexoutw ( lib_cbmio_lib_cbmio_ho_value )
ASM
lda lib_cbmio_lib_cbmio_ho_value+1
sta lib_cbmio_hb_value
jsr lib_cbmio_hexoutb
lda lib_cbmio_lib_cbmio_ho_value
sta lib_cbmio_hb_value
jsr lib_cbmio_hexoutb
ENDASM
FEND
WORD lib_cbmio_i_ptr
FUNC lib_cbmio_input ( lib_cbmio_i_ptr )
BYTE char
LET char = 0
WHILE char <> 13
ASM
jsr $ffcf
sta |char|
ENDASM
POKE lib_cbmio_i_ptr WITH char
INC lib_cbmio_i_ptr
WEND
DEC lib_cbmio_i_ptr //replace ending #13 with #0
POKE lib_cbmio_i_ptr WITH 0
FEND
ASM
lib_cbmio_ho_hextxt
!8 0,0,0
lib_cbmio_ho_hexdigits
!pet "0123456789abcdef"
ENDASM
LABEL lib_cbmio_skip
#IFEND

1566
lib/fat16/fat16lib.c65 Normal file

File diff suppressed because it is too large Load diff

686
lib/fat16/fat16lowlib.c65 Normal file
View file

@ -0,0 +1,686 @@
//------------------------------------------------------------------------
// FAT16 Library Low Level
//
// Author: Mattias Hansson
// Target: Commodore 64 with MMC64 interface
// (possible future conversions for other platforms possible)
//
// Purpose: To provide lowlevel access routines for the FAT16 Library
//
// Routines:
// lib_fat16_int_read_cluster_sector (INTERNAL)
//
//
//
//------------------------------------------------------------------------
#IFNDEF __LIB_FAT16LOW
#DEFINE __LIB_FAT16LOW = 1
GOTO lib_fat16low_skip
//------------------------------------------------------------------------
// FAT16 constants
//------------------------------------------------------------------------
WORD CONST LIB_FAT16_CLUSTER_FREE = $0000
WORD CONST LIB_FAT16_CLUSTER_END = $fff0 //this value or above = clusterchain ended
BYTE CONST LIB_FAT16_DIRENTRY_SIZE = $20
BYTE CONST LIB_FAT16_DELETED_FILE = $e5
BYTE CONST LIB_FAT16_END_OF_DIR = $00
WORD CONST LIB_FAT16_SECTOR_SIZE = 512
//******Do NOT EVER access this var as a regular variable***********
//special: this is NOT a regular variable for usage!!!!!!!!!!!!!!!!!
//The "Address" defines how many files may be open simultainously!!!
//If you want to change the number of open files, change the!!!!!!!!
//address to the number you of files you want open!!!!!!!!!!!!!!!!!!
BYTE lib_fat16_max_open_files @ __LIB_FAT16_MAX_OPEN_FILES
//******Do NOT EVER access this var as a regular variable***********
BYTE CONST LIB_FAT16_FILEMODE_READ = 0
BYTE CONST LIB_FAT16_FILEMODE_WRITE = 1
//This filemode is special as it's only valid for fclose()
//This file gets set when blockwriting another size then $512
//as further writes would not be alligned with the sectors.
BYTE CONST LIB_FAT16_FILEMODE_WRITE_ENDED = 2
//BYTE CONST LIB_FAT16_FILEMODE_READWRITE = 2 //future expansion
#IFDEF __LIB_FAT16_WRITESUPPORT
//This constant controls how many consecutive clusters will be controlled
//for beeing free when mounting a partition for read/write access.
//Generally speaking this is good when having an unfragmented disk/card
//with just a little data on it, as i shortens the mount time.
//However if you'll be writing much data (like several megabytes) you'll
//probably want to set this to $ffff (no limit) since this will not add
//any performance penalty (for searching new free clusters) when you've
//reached the limit.
#IFDEF __LIB_MMC64_OPTIMIZE_SPEED
WORD CONST LIB_FAT16_WRITE_CLUSTERS_MAX = $400
#IFEND
#IFNDEF __LIB_MMC64_OPTIMIZE_SPEED
//Explanation: if we don't cache the reads, every single cluster
//we search (in fat) will force the lib to re-read the sector again
//which will take painfully much time
#PRINT __LIB_MMC64_OPTIMIZE_SPEED combined with __LIB_FAT16_WRITESUPPORT is catastrophic performance-wise.
WORD CONST LIB_FAT16_WRITE_CLUSTERS_MAX = $10
#IFEND
#IFEND
//------------------------------------------------------------------------
// FAT16 common variables
//------------------------------------------------------------------------
//Info for developers: the lib_fat16_buffer_ptr must point out an 512 byte area
//You can choose to do so by a constant or variable.
//Default is a variable that points out an area reserved inside this library
//called "lib_fat16_fatbuffer". If you change this to a constant, remark
//the "lib_fat16_fatbuffer" area to save the unused filler.
//WORD CONST lib_fat16_buffer_ptr = $c800 //change this for your particular usage
// //(size needed is "one sector" = 512 bytes )
WORD lib_fat16_buffer_ptr
BYTE lib_fat16_sectors_per_cluster // needed for cluster reading routine
WORD lib_fat16_reserved_sectors // offset from FAT16 boot record to FATs (file allocation tables)
BYTE lib_fat16_nr_of_fats // self*sectors_per_fat = offset from FATs to the root directory
WORD lib_fat16_sectors_per_fat // (see above)
WORD lib_fat16_rootdir_entries // Max file-entries in the ROOT directory
// ( self * size of directory entry (32) ) / 512 =
// offset from ROOT directory to DATA area (where clusters begin.
// N.B. First cluster here is #2 (!))
WORD lib_fat16_rootdir_sectors // calculated size of rootdir measured in sectors.
#IFDEF __LIB_FAT16_WRITESUPPORT
WORD lib_fat16_total_clusters = 0 // ( sectors_per_fat * bytes_per_sector ($200) ) / 2 == ( sectors_per_fat * $100 ).
WORD lib_fat16_first_write_cluster = $ffff //these two are used internally by lib_fat16_int_find_unused_clusters and
WORD lib_fat16_last_write_cluster = $0000 //lib_fat16_fwriteblock to find new areas for writing bit by bit (as needed)
WORD lib_fat16_total_sectors_lo
WORD lib_fat16_total_sectors_hi
#IFEND
//Temp ZP-variables for usage in the lib.
WORD lib_fat16_tempw @ $3f
WORD lib_fat16_tempw2 @ $4b
BYTE lib_fat16_tempb @ $41
//------------------------------------------------------------------------
// 512 byte internal buffer for FAT16 operations.
//------------------------------------------------------------------------
LABEL lib_fat16_fatbuffer
ASM
!fill 512
ENDASM
//------------------------------------------------------------------------
// lib_fat16_int_read_cluster_sector (INTERNAL)
//
// Purpose:
// 1. To read out a specific sector in a specific cluster
// or
// 2. To read out a sector in the root-dir
//
// Parameters:
// lib_fat16_int_rc_cluster - cluster to read. special: 0 = root-dir
// lib_fat16_int_rc_sector - sector # inside the cluster (0..lib_fat16_sectors_per_cluster-1)
// special: if in root-dir (0..lib_fat16_rootdir_sectors-1)
// lib_fat16_int_rc_buffer_ptr - pointer, where to put the read sector in memory
// lib_fat16_int_rc_status - 0 = OK. !0 = error.
//
// Preparation: lib_fat16_mount_partition
//
// Usage of lib temp variables:
// lib_fat16_tempw2
// lib_fat16_tempb
//------------------------------------------------------------------------
WORD lib_fat16_int_rc_cluster
WORD lib_fat16_int_rc_sector
WORD lib_fat16_int_rc_buffer_ptr
BYTE lib_fat16_int_rc_status
FUNC lib_fat16_int_read_cluster_sector ( lib_fat16_int_rc_cluster lib_fat16_int_rc_sector lib_fat16_int_rc_buffer_ptr out:lib_fat16_int_rc_status )
WORD first_pos // in bytes
WORD read_count
LET first_pos = 0
LET read_count = LIB_FAT16_SECTOR_SIZE
FUNC lib_fat16_int_read_cluster_sector_part ( lib_fat16_int_rc_cluster lib_fat16_int_rc_sector lib_fat16_int_rc_buffer_ptr first_pos read_count out:lib_fat16_int_rc_status )
BYTE spc_count
WORD csect_offset_lo
BYTE csect_offset_hi
BYTE callstat //status for internal calls
LET lib_fat16_int_rc_status = 1 //indicate an error at first (we SUBEND out if things go wrong)
IF lib_fat16_int_rc_cluster == 0 //the root directory
IF lib_fat16_int_rc_sector >= lib_fat16_rootdir_sectors
SUBEND // caller tried to make us read past the root-dir region. probably an internal error.
ENDIF
//Now we must add the start sector for the rootdir with the sector requested inside it, to get the right one
ASM
clc
lda lib_fat16_rootdir_sector_lo
adc lib_fat16_int_rc_sector
sta lib_fat16_tempw2
lda lib_fat16_rootdir_sector_lo+1
adc lib_fat16_int_rc_sector+1
sta lib_fat16_tempw2+1
lda lib_fat16_rootdir_sector_hi
adc #0
sta lib_fat16_tempb
ENDASM
CALL lib_mmc64_readsector ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_rc_buffer_ptr callstat )
IF callstat != 0
SUBEND
ENDIF
GOTO lib_fat16_int_rc_exit_success
ENDIF
//Else this is a real cluster in the dataarea that's about to be read
IF lib_fat16_int_rc_sector >= lib_fat16_sectors_per_cluster
SUBEND // caller passed a sector offset beyond the end of the cluster. probably an internal error
ENDIF
//Adjust for data-area starting with cluster #2
SUBT lib_fat16_int_rc_cluster - 2 -> lib_fat16_int_rc_cluster
//Now we must multiply the clusternr with "nr of sectors per cluster" to
//get a valid sector-offset into the data-area
LET csect_offset_lo = lib_fat16_int_rc_cluster
LET csect_offset_hi = 0
LET spc_count = lib_fat16_sectors_per_cluster
//First bit unimportant (1 sector per cluster and we're done here!)
ASM
lsr |spc_count|
ENDASM
WHILE spc_count
ASM
clc
rol |csect_offset_lo|
rol |csect_offset_lo|+1
rol |csect_offset_hi|
lsr |spc_count|
ENDASM
WEND
// "real sector to access" = "start of data area" + "cluster sector offset" + lib_fat16_int_rc_sector (sector # in cluster)
//first we do "start of data area" + "cluster sector offset"
ASM
clc
lda lib_fat16_data_sector_lo
adc |csect_offset_lo|
sta lib_fat16_tempw2
lda lib_fat16_data_sector_lo+1
adc |csect_offset_lo|+1
sta lib_fat16_tempw2+1
lda lib_fat16_data_sector_hi
adc |csect_offset_hi|
sta lib_fat16_tempb
ENDASM
//now we add the lib_fat16_int_rc_sector
ASM
clc
lda lib_fat16_tempw2
adc |lib_fat16_int_rc_sector|
sta lib_fat16_tempw2
lda lib_fat16_tempw2+1
adc #0 ; we know that "sectors per cluster" cannot exceed 128.
sta lib_fat16_tempw2+1
lda lib_fat16_tempb
adc #0
sta lib_fat16_tempb
ENDASM
//Mostly we read whole sectors. However if the file ends in (( size % 0x200 )!=0) the file ends someplace inside
//a sector, and we must read the last sector partly.
CALL lib_mmc64_readsector_part ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_rc_buffer_ptr first_pos read_count callstat )
IF callstat != 0
SUBEND
ENDIF
LABEL lib_fat16_int_rc_exit_success
LET lib_fat16_int_rc_status = 0
FEND
//------------------------------------------------------------------------
// lib_fat16_int_write_cluster_sector (INTERNAL)
//
// Purpose:
// 1. To write a specific sector in a specific cluster
// or
// 2. To write a sector in the root-dir
//
// Parameters:
// lib_fat16_int_wc_cluster - cluster to write. special: 0 = root-dir
// lib_fat16_int_wc_sector - sector # inside the cluster (0..lib_fat16_sectors_per_cluster-1)
// special: if in root-dir (0..lib_fat16_rootdir_sectors-1)
// lib_fat16_int_wc_buffer_ptr - pointer, sector-data in memory to write
// lib_fat16_int_wc_status - 0 = OK. !0 = error.
//
// Preparation: lib_fat16_mount_partition
//
// Usage of lib temp variables:
// lib_fat16_tempw2
// lib_fat16_tempb
//------------------------------------------------------------------------
#IFDEF __LIB_FAT16_WRITESUPPORT
WORD lib_fat16_int_wc_cluster
WORD lib_fat16_int_wc_sector
WORD lib_fat16_int_wc_buffer_ptr
BYTE lib_fat16_int_wc_status
FUNC lib_fat16_int_write_cluster_sector ( lib_fat16_int_wc_cluster lib_fat16_int_wc_sector lib_fat16_int_wc_buffer_ptr out:lib_fat16_int_wc_status )
WORD first_pos // in bytes
WORD write_count
LET first_pos = 0
LET write_count = LIB_FAT16_SECTOR_SIZE
FUNC lib_fat16_int_write_cluster_sector_part ( lib_fat16_int_wc_cluster lib_fat16_int_wc_sector lib_fat16_int_wc_buffer_ptr first_pos write_count out:lib_fat16_int_wc_status )
BYTE spc_count
WORD csect_offset_lo
BYTE csect_offset_hi
BYTE callstat //status for internal calls
LET lib_fat16_int_wc_status = 1 //indicate an error at first (we SUBEND out if things go wrong)
IF lib_fat16_int_wc_cluster == 0 //the root directory
IF lib_fat16_int_wc_sector >= lib_fat16_rootdir_sectors
SUBEND // caller tried to make us read past the root-dir region. probably an internal error.
ENDIF
//Now we must add the start sector for the rootdir with the sector requested inside it, to get the right one
ASM
clc
lda lib_fat16_rootdir_sector_lo
adc lib_fat16_int_wc_sector
sta lib_fat16_tempw2
lda lib_fat16_rootdir_sector_lo+1
adc lib_fat16_int_wc_sector+1
sta lib_fat16_tempw2+1
lda lib_fat16_rootdir_sector_hi
adc #0
sta lib_fat16_tempb
ENDASM
CALL lib_mmc64_writesector ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_wc_buffer_ptr callstat )
IF callstat != 0
SUBEND
ENDIF
GOTO lib_fat16_int_wc_exit_success
ENDIF
//Else this is a real cluster in the dataarea that's about to be read
IF lib_fat16_int_wc_sector >= lib_fat16_sectors_per_cluster
SUBEND // caller passed a sector offset beyond the end of the cluster. probably an internal error
ENDIF
//Adjust for data-area starting with cluster #2
SUBT lib_fat16_int_wc_cluster - 2 -> lib_fat16_int_wc_cluster
//Now we must multiply the clusternr with "nr of sectors per cluster" to
//get a valid sector-offset into the data-area
LET csect_offset_lo = lib_fat16_int_wc_cluster
LET csect_offset_hi = 0
LET spc_count = lib_fat16_sectors_per_cluster
//First bit unimportant (1 sector per cluster and we're done here!)
ASM
lsr |spc_count|
ENDASM
WHILE spc_count
ASM
clc
rol |csect_offset_lo|
rol |csect_offset_lo|+1
rol |csect_offset_hi|
lsr |spc_count|
ENDASM
WEND
// "real sector to access" = "start of data area" + "cluster sector offset" + lib_fat16_int_wc_sector (sector # in cluster)
//first we do "start of data area" + "cluster sector offset"
ASM
clc
lda lib_fat16_data_sector_lo
adc |csect_offset_lo|
sta lib_fat16_tempw2
lda lib_fat16_data_sector_lo+1
adc |csect_offset_lo|+1
sta lib_fat16_tempw2+1
lda lib_fat16_data_sector_hi
adc |csect_offset_hi|
sta lib_fat16_tempb
ENDASM
//now we add the lib_fat16_int_wc_sector
ASM
clc
lda lib_fat16_tempw2
adc |lib_fat16_int_wc_sector|
sta lib_fat16_tempw2
lda lib_fat16_tempw2+1
adc #0 ; we know that "sectors per cluster" cannot exceed 128.
sta lib_fat16_tempw2+1
lda lib_fat16_tempb
adc #0
sta lib_fat16_tempb
ENDASM
//Mostly we read whole sectors. However if the file ends in (( size % 0x200 )!=0) the file ends someplace inside
//a sector, and we must read the last sector partly.
CALL lib_mmc64_writesector_part ( lib_fat16_tempw2 lib_fat16_tempb lib_fat16_int_wc_buffer_ptr first_pos write_count callstat )
IF callstat != 0
SUBEND
ENDIF
LABEL lib_fat16_int_wc_exit_success
LET lib_fat16_int_wc_status = 0
FEND
#IFEND
//------------------------------------------------------------------------
// lib_fat16_int_get_cluster_link (INTERNAL)
//
// Purpose:
// Return the next cluster nr for a given cluster.
// In the FAT the position for the current cluster holds the link
// to the next cluster in the "chain".
// special values are: $0000 unallocated, $ffef-$ffff end-of-cluster-chain
//
// Parameters:
// lib_fat16_int_nc_cluster_nr - we send in our current cluster number
// and we get the next returned in the
// same param
// lib_fat16_int_nc_status - the status operation. 0 = success
// !0 = failure (probably internal)
//
// Preparation: lib_fat16_dirnext (caller)
// (unfinished) lib_fat16_fopen
//
// Usage of lib temp variables:
// lib_fat16_tempw2
// lib_fat16_tempb
//
//
// lib_fat16_int_set_cluster_link (INTERNAL)
//
// Purpose: to update the two FAT's with a new value for the cluster-link
//
// Additional parameter:
// lib_fat16_int_nc_target_cluster_nr = the new cluster-link to write
//
// Sorry for the clutter, but this way saves a whole lot of code to
// support write-access //Mattias
//
// A note on caching: Since a fat-sector is read, updated and written back
// at once we don't need to invalidate the "caching" of the FAT-buffer.
// (a later cached read will still be valid)
//------------------------------------------------------------------------
WORD lib_fat16_int_nc_cluster_nr
BYTE lib_fat16_int_nc_status
#IFDEF __LIB_FAT16_WRITESUPPORT
BYTE lib_fat16_int_nc_writeflag
WORD lib_fat16_int_nc_target_cluster_nr
FUNC lib_fat16_int_set_cluster_link ( lib_fat16_int_nc_cluster_nr lib_fat16_int_nc_target_cluster_nr out:lib_fat16_int_nc_status )
LET lib_fat16_int_nc_writeflag = 1
#IFEND
FUNC lib_fat16_int_get_cluster_link ( io:lib_fat16_int_nc_cluster_nr out:lib_fat16_int_nc_status )
BYTE sect_offset
BYTE callstat
WORD sector_lo
BYTE sector_hi
//MHTAG 20050927 - this internal variable was replaced internally for lib_fat16_tempw2 (ZP-vars == better code)
//WORD lib_fat16_int_nc_link_offset_bytes
LET lib_fat16_int_nc_status = 1
//Clusters in the data-area are numbered from 2 (i.e. 2 = offset 0 )
IF lib_fat16_int_nc_cluster_nr < 2
SUBEND
ENDIF
//Values from $fff0 are "bad", reserved or cluster-chain end markers. (endmark>=$fff8)
//We don't care here, since we cannot follow longer anyhow.
IF lib_fat16_int_nc_cluster_nr >= LIB_FAT16_CLUSTER_END
SUBEND
ENDIF
// exp for the sector offset on disk for requested cluster entry:
// "first FAT sector" + ( (cluster_nr * "sizeof cluster entry (2 bytes in FAT16 )") / 512 )
// simplified = "first FAT sector" + ( cluster_nr / 256 ) i.e. the high byte of "cluster nr"
// div 256 = shift left 8 bits, so we simply take the highbyte and move it to the lowbyte (see below)
ASM
lda lib_fat16_int_nc_cluster_nr+1
sta |sect_offset|
ENDASM
//and now we add the "first FAT sector"
ASM
clc
lda lib_fat16_fat1_sector_lo
adc |sect_offset|
sta lib_fat16_tempw2
lda lib_fat16_fat1_sector_lo+1
adc #0
sta lib_fat16_tempw2+1
lda lib_fat16_fat1_sector_hi
adc #0
sta lib_fat16_tempb
ENDASM
LET sector_lo = lib_fat16_tempw2
LET sector_hi = lib_fat16_tempb
//read up the sector
CALL lib_mmc64_readsector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
IF callstat != 0
SUBEND //pass the read error on to the caller.
ENDIF
//calculate the position of the link
//Following the logic above the offset the the position of the link in the sector must be in the low-byte
//However since every entry is 2 bytes long, we must multiply the lowbyte with 2 to get the offset in bytes
//
//MHTAG 20050927 - all usage of lib_fat16_tempw2 below in this func is replacement for lib_fat16_int_nc_link_offset_bytes
ASM
clc
lda lib_fat16_int_nc_cluster_nr
rol
sta lib_fat16_tempw2
lda #0
rol
sta lib_fat16_tempw2+1
ENDASM
ADD lib_fat16_tempw2 + lib_fat16_buffer_ptr -> lib_fat16_tempw2
#IFDEF __LIB_FAT16_WRITESUPPORT
//if readaccess - skit this section
IF lib_fat16_int_nc_writeflag == 1
//this section updates the cluster link value and writes the fat-sector back to disk
PUTASWORD@ lib_fat16_tempw2 VALUE lib_fat16_int_nc_target_cluster_nr
CALL lib_mmc64_writesector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
IF callstat != 0
SUBEND //pass the read error on to the caller.
ENDIF
//now we must update the second FAT.
//change here to support more than 2 FATs
//reason for decision to support only 2 = VERY standard. Even linux vfat driver relies on two FATs
//24-bit addition of the size of one fat to get right offset
ASM
clc
lda |sector_lo|
adc lib_fat16_sectors_per_fat
sta |sector_lo|
lda |sector_lo|+1
adc lib_fat16_sectors_per_fat+1
sta |sector_lo|+1
lda |sector_hi|
adc #0
sta |sector_hi|
ENDASM
//and finally the actual write.
CALL lib_mmc64_writesector ( sector_lo sector_hi lib_fat16_buffer_ptr callstat )
IF callstat != 0
SUBEND //pass the read error on to the caller.
ENDIF
GOTO lib_fat16_int_nc_skipread
ENDIF
#IFEND
//return it
GETASWORD@ lib_fat16_tempw2 -> lib_fat16_int_nc_cluster_nr
#IFDEF __LIB_FAT16_WRITESUPPORT
LABEL lib_fat16_int_nc_skipread
LET lib_fat16_int_nc_writeflag = 0 //reset for next access of this function
#IFEND
LET lib_fat16_int_nc_status = 0
FEND
//------------------------------------------------------------------------
// lib_fat16_int_search_free_clusters
//
// Purpose: To find clusters on the mounted drive that are free to allocate
// for new filedata.
//
// Usage: When mounting a partition for R/W access this routine is called
// to find unused clusters, in the event that a write is issued. The
// routine searches for a "cluster hole" of max LIB_FAT16_WRITE_CLUSTERS_MAX
// continous clusters (to reduce search time). It updates the variables
// lib_fat16_first_write_cluster and lib_fat16_last_write_cluster with
// the limits of the found "cluster hole" (range of unallocated clusters)
//
// If writing has completely filled previously discovered "cluster hole"
// the write routines may call this routine again to try to find more
// unclaimed disc space to fill.
//
// Method:
// Space for writing is searched from the end of the FAT (last clusters)
// and backwards until we find free clusters. When we find a free cluster
// we set the "lib_fat16_last_write_cluster" marker and continue backwards
// for a while (until we reach a count or until we hit data again. This is
// where we set the "lib_fat16_first_write_cluster" marker.
// After this operation we know about an area between the two markers that
// consists of free clusters, ready to use for writing file-data
//------------------------------------------------------------------------
#IFDEF __LIB_FAT16_WRITESUPPORT
BYTE lib_fat16_int_fu_status
FUNC lib_fat16_int_search_free_clusters ( out:lib_fat16_int_fu_status )
WORD active_cluster
WORD active_link
WORD cluster_search_max
BYTE callstat
LET lib_fat16_int_fu_status = 1 //default = error exit
//-----------------------------------------------------
//TASK1 - find out where to start the search
LET active_cluster = lib_fat16_first_write_cluster
IF active_cluster == $ffff //if free area is uninitialized yet
LET active_cluster = lib_fat16_total_clusters //start the search at the end of the fat
ENDIF
//-----------------------------------------------------
//TASK2 - rewind until we reach:
// a) free clusters
// b) start of fat
WHILE 1
LET active_link = active_cluster
CALL lib_fat16_int_get_cluster_link ( active_link callstat )
IF callstat != 0
SUBEND //internal error
ENDIF
IF active_link == 0
BREAK
ENDIF
DEC active_cluster
WEND
IF active_cluster < 2 //disk is full. Unable to find a single free cluster!
SUBEND
ENDIF
LET lib_fat16_last_write_cluster = active_cluster
//-----------------------------------------------------
//TASK3 - rewind until we reach the end of the free area.
//LET active_link = active_cluster
LET cluster_search_max = LIB_FAT16_WRITE_CLUSTERS_MAX
WHILE active_link == LIB_FAT16_CLUSTER_FREE
DEC cluster_search_max
IF cluster_search_max == 0
BREAK
ENDIF
DEC active_cluster
LET active_link = active_cluster
CALL lib_fat16_int_get_cluster_link ( active_link callstat )
IF callstat != 0
SUBEND //internal error
ENDIF
WEND
IF active_cluster < 2
LET active_cluster = 1
ENDIF
INC active_cluster //this one was used so we need the next one (the first free)
LET lib_fat16_first_write_cluster = active_cluster
//now we should have found a area free for writing.
//MHTAG 20051114 - debug writes out the free area to the screen
//CALL lib_dbg_hexoutw ( lib_fat16_first_write_cluster )
//CALL lib_dbg_outlf
//CALL lib_dbg_hexoutw ( lib_fat16_last_write_cluster )
//CALL lib_dbg_outlf
LET lib_fat16_int_fu_status = 0 //if we got this far it's a success
FEND
#IFEND
LABEL lib_fat16low_skip
#IFEND

709
lib/fat16/mmc64lib.c65 Normal file
View file

@ -0,0 +1,709 @@
//------------------------------------------------------------------------
// MMC64 ReadWrite Library
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU GPL 2
// Language: 65CM v0.4+
// Dependencies:
// ZP usage: $fb-$fc (pointer to buffer)
// Target: Commodore 64 with MMC64 interface
//
// functions:
// ( MMC cartridge level )
// lib_mmc64_enable
// lib_mmc64_status
// ( MMC-card or SD-card level )
// lib_mmc64_cardinit
// lib_mmc64_readsector
//
// External compiler directive support:
// __LIB_MMC64_OPTIMIZE_SIZE - define this to build lib smaller
// but slower (default UNDEFINED)
// __LIB_MMC64_READONLY - define this to build the lib to support
// only read operations (decreasing it's size)
// (default UNDEFINED)
//
// Note: Some of the code is copied from Olivers Achtens democode.
// I havent removed any comments or anything and it's very clear to
// see, what comes from his paper caller "mmc64prog.txt". These
// parts of the code are, if I understand it right, in the public domain
// as just code-snippets to demonstrate how the MMC64 can be accessed.
// Just want to clarify everything, and give credit where credit due.
//
// Note2: Communication with MMC/SD in this lib is implemented to always
// Align with sectorsizes i.e. $200. i.e. AccessAddress % $200 is always
// zero(!). Every single read or write access is always $200 long.
// (Reason: This seams to keep all MMC/SD iv'e tried happy and provides
// realiable communications.)
//
// Mattias Hansson
//------------------------------------------------------------------------
#IFNDEF __LIB_MMC64
#DEFINE __LIB_MMC64 = 1
//Define __LIB_MMC64_OPTIMIZE_SIZE externally to optimize for size instead of speed.
#IFNDEF __LIB_MMC64_OPTIMIZE_SIZE
#DEFINE __LIB_MMC64_OPTIMIZE_SPEED = 1
#IFEND
//This define is needed to include the code needed for writesupport.
#IFNDEF __LIB_MMC64_READONLY
#PRINT Write access in mmc65lib.c65 is experimental!!!
#DEFINE __LIB_MMC64_WRITESUPPORT = 1
#IFEND
GOTO lib_mmc64_skip
//------------------------------------------------------------------------
// buffer pointer zeropage variable used for data transfer function
//------------------------------------------------------------------------
WORD lib_mmc64_buffer_ptr @ $fb
//------------------------------------------------------------------------
// MMC64 reader status constants
//
// Everything except LIB_MMC64_R_OK
// is an error.
// Constants in order of importance.
//------------------------------------------------------------------------
BYTE CONST LIB_MMC64_R_OK = 0
BYTE CONST LIB_MMC64_R_BUSY = 1
BYTE CONST LIB_MMC64_R_NOCARD = 2
BYTE CONST LIB_MMC64_R_FLASH_ENABLED = 3
//------------------------------------------------------------------------
// lib_mmc64_enable
//
// Purpose: Enables the MMC64 cartridge
// Params: none.
// Preparation: none.
//------------------------------------------------------------------------
FUNC lib_mmc64_enable
ASM
;(re)enable MMC64
lda #$0A
sta $df13
lda #$1C
sta $df13
;enable MMC64 with initial settings
lda $df11 ;get contents of MMC64 control register
and #%10111011 ;set 250khz & write trigger mode
ora #%00000010 ;disable card select
sta $df11 ;update control register
ENDASM
FEND
//------------------------------------------------------------------------
// lib_mmc64_status
//
// Purpose:
// To get actual status codes from the reader controller.
// Parameters:
// out:lib_mmc64_st_value - Value of one of the
// "MMC64 reader status constants" defined
// above.
//
// Preparation: lib_mmc64_enable
//
//------------------------------------------------------------------------
BYTE lib_mmc64_st_value
FUNC lib_mmc64_status ( out:lib_mmc64_st_value )
ASM
lda $df12
and #%00111001
asl
asl
asl ; flash jumper enabled bit. true if set!
bcc lib_mmc64_rstatus_l1 ; if NOT flash jumper is set
;flash jumper is set!
lda #3
sta lib_mmc64_st_value
rts
lib_mmc64_rstatus_l1
asl
asl ; card inserted bit. card NOT inserted if set
bcc lib_mmc64_rstatus_l2 ; if card IS inserted
; no card inserted!
lda #2
sta lib_mmc64_st_value
rts
lib_mmc64_rstatus_l2
and #%00100000 ; the current position of the busy-bit after ASL's above
beq lib_mmc64_rstatus_l3 ; after the and-operation we have a 0-value if all is ok, so we store it!
lda #1 ; card is busy
lib_mmc64_rstatus_l3
sta lib_mmc64_st_value
ENDASM
FEND
//------------------------------------------------------------------------
// lib_mmc64_set512blocks
//
// Send CMD16 to MMC/SD to set the blocksize to $200 (512) which is the
// only transfer size used in this lib.
// This size is the defaut when doing read operations, but must be
// exclusivly set when attempting writes to the MMC/SD
//------------------------------------------------------------------------
#IFDEF __LIB_MMC64_WRITESUPPORT
FUNC lib_mmc64_set512blocks
ASM
ldy #$09
lib_mmc64_s5_l1
lda lib_mmc64_s5_cmd16-1,y
sta $df10
dey
bne lib_mmc64_s5_l1
ENDASM
FEND
ASM
lib_mmc64_s5_cmd16
!8 $ff,$ff,$ff,$00,$02,$00,$00,$50,$ff ;CMD16
ENDASM
#IFEND
//------------------------------------------------------------------------
// lib_mmc64_cardinit
//
// Purpose: Reset the SD||MMC card and set it up in SPI mode
// Params: none.
// Preparation: lib_mmc64_enable,
// (lib_mmc64_status
// to know that there is a working card in the slot)
//
//------------------------------------------------------------------------
FUNC lib_mmc64_cardinit
ASM
lib_mmc64_reset
ldx #$0a ;initialize counter (10*8 pulses)
ldy #$ff ;initialize value to be written (bits must be all high)
lib_mmc64_reset_l1
sty $df10 ;send 8 pulses to the card
lib_mmc64_reset_l2
lda $df12 ;we catch the status register
and #$01 ;all bits shiftet out?
bne lib_mmc64_reset_l2 ;nope, so wait
dex ;decrement counter
bne lib_mmc64_reset_l1 ;until 80 pulses sent
lda $df11 ;pull card chip select line down for SPI communication
and #%11111101
sta $df11
lib_mmc64_reset_l6
ldy #$09 ;we send 8 command bytes to the card
lib_mmc64_reset_l3
lda lib_mmc64_reset_cmdtxt0-1,y ;grab command byte
sta $df10 ;and fire it to the card
lib_mmc64_reset_l4
lda $df12 ;we check the busy bit
and #$01 ;to ensure that the transfer is safe
bne lib_mmc64_reset_l4
dey ;decrease command counter
bne lib_mmc64_reset_l3 ;until the entire command has been sent
lda $df10 ;now we check the card response
and #$01 ;did it accept the command ?
bne lib_mmc64_reset_l5 ;ok, everything is fine !
lda $df12 ;is there a card at all ?
and #$08
beq lib_mmc64_reset_l6 ;yup, so we just resend the command
lib_mmc64_reset_l5
lib_mmc64_reset_l7
ldy #$09 ;we send 8 command bytes to the card
lib_mmc64_reset_l8
lda lib_mmc64_reset_cmdtxt1-1,y ;grab command byte
sta $df10 ;and fire it to the card
lib_mmc64_reset_l9
lda $df12 ;we check the busy bit
and #$01 ;to ensure that the transfer is safe
bne lib_mmc64_reset_l9
dey ;decrease command counter
bne lib_mmc64_reset_l8 ;until entire command has been sent
lda $df10 ;did the card left its init state?
and #$01
bne lib_mmc64_reset_l7 ;no, so we resend the command to check it again
;Enable 8Mhz mode
lda $df11
ora #%00000100
sta $df11
ENDASM
#IFDEF __LIB_MMC64_WRITESUPPORT
CALL lib_mmc64_set512blocks
#IFEND
FEND
ASM
lib_mmc64_reset_cmdtxt0
!8 $ff,$ff,$95,$00,$00,$00,$00,$40,$ff ;CMD0
lib_mmc64_reset_cmdtxt1
!8 $ff,$ff,$ff,$00,$00,$00,$00,$41,$ff ;CMD1
ENDASM
//------------------------------------------------------------------------
// lib_mmc64_readsector
//
// Purpose: To read a sector specified by the 24-bit number sent in.
// (65CM does not support larger types than 16-bit so we have
// to use two.) and put the data where the buffer pointer points.
//
// lib_mmc64_readsector_part
//
// Purpose: Like above but optionally be able to choose what part of the
// sector caller wants to read. This call was implemented to
// allow FAT16 to:
// 1. Read files to their real end (not the end of the last sector of a file)
// 2. fgetc()
//
// Params:
// lib_mmc64_rs_sectornr_lo = the two lowest bytes of the 24-bit sector#
// lib_mmc64_rs_sectornr_hi = the highest byte of the 24-bit sector#
// lib_mmc64_buffer_ptr = the address where to put the read data
// lib_mmc64_rs_status = after a completed operation the caller can
// check if it went OK. 0=OK. !0=fail!
//
// Params (extended for "lib_mmc64_readsector_part"):
// WORD first_pos - first byte in the sector to write to memory
// WORD read_count - how many bytes to write to target memory
//
// Preparation: lib_mmc64_cardinit
//------------------------------------------------------------------------
WORD lib_mmc64_rs_sectornr_lo
BYTE lib_mmc64_rs_sectornr_hi
BYTE lib_mmc64_rs_status
FUNC lib_mmc64_readsector ( lib_mmc64_rs_sectornr_lo lib_mmc64_rs_sectornr_hi lib_mmc64_buffer_ptr out:lib_mmc64_rs_status )
WORD first_pos // in bytes
WORD read_count
LET first_pos = 0
LET read_count = 512
FUNC lib_mmc64_readsector_part ( lib_mmc64_rs_sectornr_lo lib_mmc64_rs_sectornr_hi lib_mmc64_buffer_ptr first_pos read_count out:lib_mmc64_rs_status )
WORD totalread
BYTE readbyte
LET lib_mmc64_rs_status = 1
// If what we are reading up is to the internal buffer
// We compare the sector# sent in with the last sector#
// read there. We don't need to read it up if it's equal.
// Note: this code relies on lib_fat16. Not nice, but
// It'll do for now.
// Note2: this is not neccessary for normal operation
// but will certainly speed up continous read-speed when
// used on a non-fragmented file, a whole lot.
// (without optimization it reads up a sector from the
// FAT everytime we change cluster, as it's unaware of
// whats in the fat_buffer for the moment)
// Therefore it's only included if compiled with speed
// optimization.
#IFDEF __LIB_MMC64_OPTIMIZE_SPEED
ASM
lda lib_mmc64_buffer_ptr
cmp #<lib_fat16_fatbuffer
bne lib_mmc64_rs_buffnotequal_skip
lda lib_mmc64_buffer_ptr+1
cmp #>lib_fat16_fatbuffer
bne lib_mmc64_rs_buffnotequal_skip
; here we know that the pointer sent in is indeed the internal FAT buffer.
lda lib_mmc64_rs_sectornr_lo
cmp lib_mmc64_rs_oldsector
bne lib_mmc64_rs_storenewbuffsector
lda lib_mmc64_rs_sectornr_lo+1
cmp lib_mmc64_rs_oldsector+1
bne lib_mmc64_rs_storenewbuffsector
lda lib_mmc64_rs_sectornr_hi
cmp lib_mmc64_rs_oldsector+2
bne lib_mmc64_rs_storenewbuffsector
lda #0
sta lib_mmc64_rs_status
rts ; if everything matched we just saved the caller the whole readup
; of the sector in question.
lib_mmc64_rs_storenewbuffsector
lda lib_mmc64_rs_sectornr_lo
sta lib_mmc64_rs_oldsector
lda lib_mmc64_rs_sectornr_lo+1
sta lib_mmc64_rs_oldsector+1
lda lib_mmc64_rs_sectornr_hi
sta lib_mmc64_rs_oldsector+2
jmp lib_mmc64_rs_buffnotequal_skip
lib_mmc64_rs_oldsector
!24 $ffffff
lib_mmc64_rs_buffnotequal_skip
ENDASM
#IFEND //__LIB_MMC64_OPTIMIZE_SPEED
ASM
;first we recalc the sector nr to byte nr (that the card requires)
;since a sector of a MMC or SD card is 512 bytes it's "shift left 9"
;shift left 8 is done by just shifting the bytes (naturally) which
;leaves us 1 shift left to do.
clc
rol lib_mmc64_rs_sectornr_lo
rol lib_mmc64_rs_sectornr_lo+1
rol lib_mmc64_rs_sectornr_hi
lda #0
sta lib_mmc64_rs_bytenr
lda lib_mmc64_rs_sectornr_lo
sta lib_mmc64_rs_bytenr+1
lda lib_mmc64_rs_sectornr_lo+1
sta lib_mmc64_rs_bytenr+2
lda lib_mmc64_rs_sectornr_hi
sta lib_mmc64_rs_bytenr+3
ldy #$09
lib_mmc64_rs_l1
lda lib_mmc64_rs_cmd17-1,y
sta $df10
dey
bne lib_mmc64_rs_l1
lib_mmc64_rs_l2
lda #$ff
sta $df10 ;write all high bits
lda $df10 ;to give the possibility to respond
cmp #$fe ;has it started?
beq lib_mmc64_rs_l4 ; start receive
cmp #0
beq lib_mmc64_rs_l2 ; still waiting for card to answer
cmp #$ff
beq lib_mmc64_rs_l2 ; still waiting for card to answer
jmp lib_mmc64_rs_error_exit ; Serious error - sector could not be read.
; (normaly this is because we've called the
; blockread CMD17 with a wrong param!)
lib_mmc64_rs_l4 ; start receive
lda $df11 ;set MMC64 into read trigger mode
ora #%01000000 ;which means every read triggers a SPI transfer
sta $df11
lda $df10 ;we have to start with one dummy read here
ENDASM
#IFDEF __LIB_MMC64_OPTIMIZE_SPEED
//If we're about to read the whole sector we use this routine!
//It's faster than the one below, 'coz it does not check for
//anything except the $200 (512) limit.
IF first_pos == 0
IF read_count == 512
ASM
ldx #$02 ;set up counters
ldy #$00
lib_mmc64_rs_l3
lda $df10 ;get data byte from card
sta (lib_mmc64_buffer_ptr),y ;store it into memory ( memptr has to be initialized)
iny ;have we copied 256 bytes ?
bne lib_mmc64_rs_l3 ;nope, so go on!
inc lib_mmc64_buffer_ptr+1 ;increase memory pointer for next 256 bytes
dex ;have we copied 512 bytes ?
bne lib_mmc64_rs_l3
ENDASM
GOTO lib_mmc64_rs_skipelse1
ENDIF
ENDIF
#IFEND //__LIB_MMC64_OPTIMIZE_SPEED
//ELSE - we want to read just a part of the sector. so this routine is used instead
LET totalread = 0
WHILE totalread < 512
IF first_pos == 0
IF read_count == 0
GOTO lib_mmc64_rs_dummyread
ENDIF
PEEK $df10 -> readbyte
POKE lib_mmc64_buffer_ptr , readbyte
INC lib_mmc64_buffer_ptr
DEC read_count
ELSE //prepare for first position
DEC first_pos
LABEL lib_mmc64_rs_dummyread
ASM
lda $df10 ; dummyread a byte (to move forward for the desired offset)
ENDASM
ENDIF
INC totalread
WEND
LABEL lib_mmc64_rs_skipelse1
ASM
lda $df10 ;we have to end the data transfer with one dummy read
lda $df11 ;now we put the hardware back into write trigger mode again
and #%10111111
sta $df11
ENDASM
LET lib_mmc64_rs_status = 0 // if we got this far it's a successful read!
LABEL lib_mmc64_rs_error_exit
FEND
ASM
;!pet "debugtxt>"
lib_mmc64_rs_cmd17
;Outline of the command (read sector 0)
;!8 $ff,$ff,$ff,$00,$00,$00,$00,$51,$ff ;CMD17
; ^^^^^^^^^^^^^^^ <- 32 bit byte offset where we want to read
; In this lib we always send in byte-offsets corresponding to a sector start.i (i.e. $200*sectornr)
;
;Start of command
!8 $ff,$ff,$ff
lib_mmc64_rs_bytenr
!32 0
!8 $51,$ff ;CMD17
ENDASM
//------------------------------------------------------------------------
// lib_mmc64_writesector
//
// Purpose: To write a sector specified by the 24-bit number sent in.
// (65CM does not support larger types than 16-bit so we have
// to use two.) data for the write is taken from where
// lib_mmc64_buffer_ptr points and onwards $200(512) bytes.
//
// lib_mmc64_writesector_part
//
// Purpose: Like above but optionally be able to choose what part of the
// sector caller wants to write. This call was implemented to
// allow FAT16 to write just the part of the sector that represents
// the actual EOF in the file. (rest will be zero-filled)
//
// Note: Mem-pointer points to the actual area to be written.
// I.e. first_pos equals byte @ lib_mmc64_buffer_ptr
//
// Params:
// lib_mmc64_ws_sectornr_lo = the two lowest bytes of the 24-bit sector#
// lib_mmc64_ws_sectornr_hi = the highest byte of the 24-bit sector#
// lib_mmc64_buffer_ptr = the address where to read data for writing
// lib_mmc64_ws_status = after a completed operation the caller can
// check if it went OK. 0=OK. !0=fail!
//
// Params (extended for "lib_mmc64_readsector_part"):
// WORD first_pos - first byte in the sector to write to memory
// WORD read_count - how many bytes to write to target memory
//
// Preparation: lib_mmc64_cardinit
//------------------------------------------------------------------------
#IFDEF __LIB_MMC64_WRITESUPPORT
//Block write command - set up a 512 byte transfer to the SD Card from $00000100 to $000002ff
WORD lib_mmc64_ws_sectornr_lo
BYTE lib_mmc64_ws_sectornr_hi
BYTE lib_mmc64_ws_status
FUNC lib_mmc64_writesector ( lib_mmc64_ws_sectornr_lo lib_mmc64_ws_sectornr_hi lib_mmc64_buffer_ptr out:lib_mmc64_ws_status )
WORD first_pos // in bytes
WORD read_count
LET first_pos = 0
LET read_count = 512
FUNC lib_mmc64_writesector_part ( lib_mmc64_ws_sectornr_lo lib_mmc64_ws_sectornr_hi lib_mmc64_buffer_ptr first_pos read_count out:lib_mmc64_ws_status )
WORD totalwritten
BYTE writebyte
LET lib_mmc64_ws_status = 1
//To make writesector compatible with the internal
//buffer-caching done in readsector we must update
//the pointers to the "oldsector". A write will
//always be issued though
#IFDEF __LIB_MMC64_OPTIMIZE_SPEED
ASM
lda lib_mmc64_buffer_ptr
cmp #<lib_fat16_fatbuffer
bne lib_mmc64_ws_buffnotequal_skip
lda lib_mmc64_buffer_ptr+1
cmp #>lib_fat16_fatbuffer
bne lib_mmc64_ws_buffnotequal_skip
; here we know that the pointer sent in is indeed the internal FAT buffer.
;here we write down (for eventual future caller request)
;what sector the data in the buffer will represent after we write it
lda lib_mmc64_ws_sectornr_lo
sta lib_mmc64_rs_oldsector
lda lib_mmc64_ws_sectornr_lo+1
sta lib_mmc64_rs_oldsector+1
lda lib_mmc64_ws_sectornr_hi
sta lib_mmc64_rs_oldsector+2
lib_mmc64_ws_buffnotequal_skip
ENDASM
#IFEND //__LIB_MMC64_OPTIMIZE_SPEED
ASM
;first we recalc the sector nr to byte nr (that the card requires)
;since a sector of a MMC or SD card is 512 bytes it's "shift left 9"
;shift left 8 is done by just shifting the bytes (naturally) which
;leaves us 1 shift left to do.
clc
rol lib_mmc64_ws_sectornr_lo
rol lib_mmc64_ws_sectornr_lo+1
rol lib_mmc64_ws_sectornr_hi
lda #0
sta lib_mmc64_ws_bytenr
lda lib_mmc64_ws_sectornr_lo
sta lib_mmc64_ws_bytenr+1
lda lib_mmc64_ws_sectornr_lo+1
sta lib_mmc64_ws_bytenr+2
lda lib_mmc64_ws_sectornr_hi
sta lib_mmc64_ws_bytenr+3
ENDASM
//Now we send CMD24 (Write single sector command) to MMC/SD
ASM
ldy #$09
lib_mmc64_ws_l1
lda lib_mmc64_ws_cmd24-1,y
sta $df10
dey
bne lib_mmc64_ws_l1
ldx #$ff ;we send the sequence $ff,$ff,$fe to start the transfer
stx $df10
stx $df10
dex
stx $df10
ENDASM
//If we're about to write the whole sector we use this routine
//just like the write routine
IF first_pos == 0
IF read_count == 512
ASM
;And now we can write the data to the card:
ldx #$02 ;set up counters
ldy #$00
lib_mmc64_ws_l2
lda (lib_mmc64_buffer_ptr),y ;get byte from C64 memory
sta $df10 ;give it to the card
iny ;have we copied 256 bytes ?
bne lib_mmc64_ws_l2
inc lib_mmc64_buffer_ptr+1 ;increase memory pointer for next 256 bytes
dex ;have we copied 512 bytes ?
bne lib_mmc64_ws_l2
ENDASM
GOTO lib_mmc64_ws_skipelse1
ENDIF
ENDIF
//ELSE - we want to write just a part of the sector. so this routine is used instead
LET totalwritten = 0
WHILE totalwritten < 512
IF first_pos == 0
IF read_count == 0
GOTO lib_mmc64_ws_dummywrite
ENDIF
PEEK lib_mmc64_buffer_ptr -> writebyte
POKE $df10 , writebyte
INC lib_mmc64_buffer_ptr
DEC read_count
GOTO lib_mmc64_ws_skipelse2
ENDIF
//ELSE prepare for first position
DEC first_pos
LABEL lib_mmc64_ws_dummywrite
ASM
lda #0
sta $df10 ; dummywrite NULL to rest of the sector.
ENDASM
LABEL lib_mmc64_ws_skipelse2
INC totalwritten
WEND
ASM
lib_mmc64_ws_skipelse1
lda #$ff ;send 2 sync bytes
sta $df10
sta $df10
lib_mmc64_ws_l3
sta $df10 ;wait until card has left busy state
ldx $df10
cpx #$ff
bne lib_mmc64_ws_l3
ENDASM
LET lib_mmc64_ws_status = 0
FEND
ASM
lib_mmc64_ws_cmd24
!8 $ff,$ff,$ff
lib_mmc64_ws_bytenr
!32 0 ; byte offset on card to write
!8 $58,$ff ;CMD24
ENDASM
#IFEND
LABEL lib_mmc64_skip
#IFEND

101
lib/koalalib.c65 Normal file
View file

@ -0,0 +1,101 @@
//-------------------------------------------------------
//
// KOALALIB
//
// Library that shows koala pictures
//
// Author: Mattias Hansson
// License: GPL2
// Compiler version: c65cm, v.04+
// ZP usage: $9e-$9f, $fb-$ff
//
//-------------------------------------------------------
#IFNDEF __LIB_KOALA
#DEFINE __LIB_KOALA = 1
#INCLUDE <c64scr.c65>
#INCLUDE <c64defs.c65>
GOTO lib_koala_skip
WORD lib_koala_skpicptr @ $fc
//-------------------------------------------------------
//
//-------------------------------------------------------
FUNC lib_koala_show ( io:lib_koala_skpicptr )
// Koala format description (excluding CBM loading address)
// Bitmap: $0000-$1f3f (0-7999)
// Videoram: $1f40-$2327 (8000-8999)
// Colorram: $2328-$270f (9000-9999)
// Background: $2710 (10000)
WORD lib_koala_skpoke @ $9e
BYTE lib_koala_sktemp @ $fb
GOSUB lib_c64scr_blank
//-----------------------------------------
// Copy picture data to the right locations
//-----------------------------------------
LET lib_koala_skpoke = $e000
WHILE lib_koala_skpoke < $e000+8000
PEEK lib_koala_skpicptr -> lib_koala_sktemp
POKE lib_koala_skpoke , lib_koala_sktemp
INC lib_koala_skpicptr
INC lib_koala_skpoke
WEND
LET lib_koala_skpoke = $c000
WHILE lib_koala_skpoke < $c000+1000
PEEK lib_koala_skpicptr -> lib_koala_sktemp
POKE lib_koala_skpoke , lib_koala_sktemp
INC lib_koala_skpicptr
INC lib_koala_skpoke
WEND
LET lib_koala_skpoke = colorram
WHILE lib_koala_skpoke < colorram+1000
PEEK lib_koala_skpicptr -> lib_koala_sktemp
POKE lib_koala_skpoke , lib_koala_sktemp
INC lib_koala_skpicptr
INC lib_koala_skpoke
WEND
//background color (one of the colors in MCB mode)
PEEK lib_koala_skpicptr -> lib_koala_sktemp
POKE vic2+$21 , lib_koala_sktemp
//----------------------------
// Setup VIC2 to show picture
//----------------------------
//Setup bank 4 ($c000-$ffff) for VIC-II (bits 0 and 1 negated)
PEEK cia2 -> lib_koala_sktemp
AND lib_koala_sktemp WITH $fc -> lib_koala_sktemp
POKE cia2 , lib_koala_sktemp
//Setup bitmap @ $e000 and videoram @ $c000
PEEK vic2+$18 -> lib_koala_sktemp
AND lib_koala_sktemp WITH 7 -> lib_koala_sktemp
OR lib_koala_sktemp WITH 8 -> lib_koala_sktemp
POKE vic2+$18 , lib_koala_sktemp
//Setup multicolor graphics
PEEK vic2+$11 -> lib_koala_sktemp
OR lib_koala_sktemp WITH 32 -> lib_koala_sktemp
POKE vic2+$11 , lib_koala_sktemp
PEEK vic2+$16 -> lib_koala_sktemp
OR lib_koala_sktemp WITH 16 -> lib_koala_sktemp
POKE vic2+$16 , lib_koala_sktemp
GOSUB lib_c64scr_show
FEND
LABEL lib_koala_skip
#IFEND

67
lib/libsc.c65 Normal file
View file

@ -0,0 +1,67 @@
//------------------------------------------------------------------------
// Library shortcuts Library
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies: (all libs might be included, selectively)
//
// Purpose: To shorten lib names to normal usage length
//------------------------------------------------------------------------
#IFNDEF __LIB_SC
#DEFINE __LIB_SC = 1
#IFDEF __LIB_FAT16
#DEFINE mount = lib_fat16_mount_partition
#DEFINE fopen = lib_fat16_fopen
#DEFINE fclose = lib_fat16_fclose
#DEFINE fcloses = lib_fat16_fcloses
#DEFINE fblockread = lib_fat16_fblockread
#DEFINE fblockwrite = lib_fat16_fblockwrite
#DEFINE chdir = lib_fat16_chdir
#DEFINE dirstart = lib_fat16_dirstart
#DEFINE dirnext = lib_fat16_dirnext
#IFEND
#IFDEF __LIB_CBMIO
#DEFINE print = lib_cbmio_print
#DEFINE printlf = lib_cbmio_printlf
#DEFINE hexoutb = lib_cbmio_hexoutb
#DEFINE hexoutw = lib_cbmio_hexoutw
#DEFINE lf = lib_cbmio_lf
#DEFINE home = lib_cbmio_home
#DEFINE input = lib_cbmio_input
#IFEND
#IFDEF __LIB_STRING
#DEFINE strlen = lib_string_strlen
#DEFINE strchr = lib_string_strchr
#DEFINE strcpy = lib_string_strcpy
#DEFINE stpcpy = lib_string_stpcpy
#DEFINE strncpy = lib_string_strncpy
#DEFINE strcmp = lib_string_strcmp
#DEFINE memset = lib_string_memset
#DEFINE strcat = lib_string_strcat
#DEFINE strncat = lib_string_strncat
#IFEND
#IFDEF __LIB_STRLIST
#DEFINE sl.count = lib_strlist_count
#DEFINE sl.get = lib_strlist_get
#DEFINE sl.strget = lib_strlist_strget
#DEFINE sl.add = lib_strlist_add
#DEFINE sl.free = lib_strlist_free
#IFEND
#IFDEF __LIB_STRLIST2
#DEFINE str_to_strlist = lib_strlist2_str_to_strlist
#IFEND
#IFDEF __LIB_MEM
#DEFINE malloc = lib_mem_malloc
#DEFINE free = lib_mem_free
#IFEND
#IFEND

304
lib/memlib.c65 Normal file
View file

@ -0,0 +1,304 @@
//-----------------------------------------------------------
//
// Memory library ( Malloc, Free )
//
//
// Author: Mattias Hansson
// Copyright (c) : 2000-2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies:
// Target: generic 6502
//
// Purpose: Provide some functions the help to create, update and access
// a linked list of strings.
//-----------------------------------------------------------
//-----------------------------------------------------------
// Internals:
//
// Makes heap-handling in a defined part of the memory.
//
// Requested memory is placed "from bottom upwards".
// Allocation entries are written from the top downwards.
//
// layout:
//
// Bottom ( e.g. $2000-$2fff )
// --------------
// $2000
// .
// . area 1 allocated by application
// .
// $2020
// $2021
// .
// . area 2 allocated by application
// .
// $2025
//
// .
// .
// .
//
// $2ff8 + $2ff9 = $2025 end of area 2
// $2ffa + $2ffb = $2021 start of area 2
// $2ffc + $2ffd = $2020 end of area 1
// $2ffe + $2fff = $2000 start of area 1
#IFNDEF __LIB_MEM
#DEFINE __LIB_MEM = 1
#IFNDEF __LIB_MEM_START
#DEFINE __LIB_MEM_START = $$5000
#IFEND
#IFNDEF __LIB_MEM_END
#DEFINE __LIB_MEM_END = $$9fff
#IFEND
#IFNDEF NULL
#DEFINE NULL = 0
#IFEND
GOTO lib_memlib_skip
WORD CONST lib_memstart = __LIB_MEM_START
WORD CONST lib_memend = __LIB_MEM_END
WORD lib_alloctable_end
//-----------------------------------------------------------
// Init: sets upp things for the lib in mem before start
//-----------------------------------------------------------
FUNC lib_mem_init
//set allocation table end start value
LET lib_alloctable_end GET lib_memend
INC lib_alloctable_end
FEND
//-----------------------------------------------------------
// Malloc: alloc memory of size size_t
// Address to allocated space is returned in result
//-----------------------------------------------------------
FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
WORD maxsize //max possible size now in heap
WORD newspaceb
WORD newspacee
WORD newtableend
DEC size_t // 1 = e.g. $2001-$2001 ( $2001 - $2001 = 0 )
LET newtableend GET lib_alloctable_end
SUBT newtableend - 4 -> newtableend
IF lib_alloctable_end > lib_memend THEN // only when first item!
SUBT newtableend - lib_memstart -> maxsize
IF maxsize < size_t THEN
//error handling: return NULL pointer!
LET result GET NULL
GOTO lib_malloc_exit
ENDIF
LET newspaceb GET lib_memstart
ADD newspaceb + size_t -> newspacee
//insert first record and recalc lib_alloctable_end
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspaceb
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspacee
LET result GET newspaceb
GOTO lib_malloc_exit;
ENDIF
//else if this is not the first item then:
WORD lib_malloc_ptr
WORD lib_malloc_spaceend
WORD lib_malloc_spacestart
LET lib_malloc_ptr GET lib_memend
SUBT lib_malloc_ptr - 3 -> lib_malloc_ptr
GETASWORD@ lib_malloc_ptr -> lib_malloc_spaceend
LET newspaceb GET NULL //if not found afterwards
WHILE 1
//if we reached the last record in the alloc-table
IF lib_malloc_ptr = lib_alloctable_end THEN
//is there sufficient space left @ top of heap?
INC lib_malloc_spaceend
SUBT newtableend - lib_malloc_spaceend -> maxsize
IF maxsize > size_t THEN
LET newspaceb GET lib_malloc_spaceend
ADD size_t + newspaceb -> newspacee
ENDIF
BREAK
ENDIF
DEC lib_malloc_ptr
DEC lib_malloc_ptr
GETASWORD@ lib_malloc_ptr -> lib_malloc_spacestart
SUBT lib_malloc_spacestart - lib_malloc_spaceend -> maxsize
DEC maxsize
//if we found a space big enough for the allocation
IF maxsize > size_t THEN
//Save the pointer to the new space end the pointer in the alloctable
LET newspaceb GET lib_malloc_spaceend
INC newspaceb // must start @ the next byte
ADD size_t + newspaceb -> newspacee
INC lib_malloc_ptr // set it to start of record! ( for move )
// INC lib_malloc_ptr
BREAK
ENDIF
DEC lib_malloc_ptr
DEC lib_malloc_ptr
GETASWORD@ lib_malloc_ptr -> lib_malloc_spaceend
WEND
IF newspaceb <> NULL THEN // allocate @ found space
IF lib_malloc_ptr = lib_alloctable_end THEN //allocate @ end of table
//insert record and recalc lib_alloctable_end
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspaceb
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspacee
//return new pointer
LET result GET newspaceb
GOTO lib_malloc_exit
ENDIF
//else = insert into alloctable
WORD lib_malloc_from
WORD lib_malloc_to
BYTE lib_malloc_byte
// move the rest of the stack
LET lib_malloc_from GET lib_alloctable_end
LET lib_malloc_to GET newtableend
WHILE 1
PEEK lib_malloc_from -> lib_malloc_byte
POKE lib_malloc_to WITH lib_malloc_byte
IF lib_malloc_from = lib_malloc_ptr THEN
BREAK
ENDIF
INC lib_malloc_from
INC lib_malloc_to
WEND
//insert record and recalc lib_alloctable_end
DEC lib_malloc_ptr
PUTASWORD@ lib_malloc_ptr VALUE newspaceb
DEC lib_malloc_ptr
DEC lib_malloc_ptr
PUTASWORD@ lib_malloc_ptr VALUE newspacee
LET lib_alloctable_end GET newtableend
//return new pointer
LET result GET newspaceb
ENDIF
IF newspaceb = NULL THEN // return null pointer
LET result GET NULL
GOTO lib_malloc_exit
ENDIF
LABEL lib_malloc_exit
INC size_t
FEND
//-----------------------------------------------------------
// Free: free alloced memory at m_ptr
//-----------------------------------------------------------
FUNC lib_mem_free ( {WORD m_ptr} )
WORD ptr
WORD startaddr
WORD lastrecfirst
WORD from
WORD to
BYTE tempb
IF lib_alloctable_end > lib_memend THEN // no entries
GOTO lib_free_exit
ENDIF
LET ptr GET lib_memend
DEC ptr
ADD 2 + lib_alloctable_end -> lastrecfirst
WHILE 1
GETASWORD@ ptr -> startaddr
IF startaddr = m_ptr THEN //found allocation!
//remove it!
IF ptr = lastrecfirst THEN
ADD 4 + lib_alloctable_end -> lib_alloctable_end
BREAK
ENDIF
LET to GET ptr
INC to
SUBT to - 4 -> from
WHILE 1
PEEK from -> tempb
POKE to WITH tempb
IF from = lib_alloctable_end THEN
BREAK
ENDIF
DEC from
DEC to
WEND
ADD 4 + lib_alloctable_end -> lib_alloctable_end
BREAK
ENDIF
DEC ptr
DEC ptr
IF ptr = lib_alloctable_end THEN
BREAK
ENDIF
DEC ptr
DEC ptr
WEND
LABEL lib_free_exit
FEND
LABEL lib_memlib_skip
#IFEND

87
lib/multdivlib.c65 Normal file
View file

@ -0,0 +1,87 @@
//------------------------------------------------
// Mult/Div routines, taken from "The Fridge"
// Made with 65CM interfaces
//------------------------------------------------
#IFNDEF __LIB_MULTDIV
#DEFINE __LIB_MULTDIV = 1
GOTO lib_multdiv_skip
WORD lib_multdiv_acc
WORD lib_multdiv_aux
WORD lib_multdiv_ext
//------------------------------------------------
// * MULTIPLY ROUTINE
//* ACC*AUX -> [ACC,EXT] (low,hi) 32 bit result
//------------------------------------------------
FUNC lib_multdiv_mult ( io:lib_multdiv_acc lib_multdiv_aux )
FUNC lib_multdiv_mult32 ( io:lib_multdiv_acc lib_multdiv_aux out:lib_multdiv_ext )
ASM
lda #0
sta lib_multdiv_ext+1
ldy #$11
clc
lib_multdiv_l1
ror lib_multdiv_ext+1
ror
ror lib_multdiv_acc+1
ror lib_multdiv_acc
bcc lib_multdiv_mul2
clc
adc lib_multdiv_aux
pha
lda lib_multdiv_aux+1
adc lib_multdiv_ext+1
sta lib_multdiv_ext+1
pla
lib_multdiv_mul2
dey
bne lib_multdiv_l1
sta lib_multdiv_ext
ENDASM
FEND
//------------------------------------------------
// * DIVIDE ROUTINE
//* ACC/AUX -> ACC, remainder in EXT
//------------------------------------------------
FUNC lib_multdiv_div ( io:lib_multdiv_acc lib_multdiv_aux )
FUNC lib_multdiv_divmod ( io:lib_multdiv_acc lib_multdiv_aux out:lib_multdiv_ext )
ASM
lda #0
sta lib_multdiv_ext+1
ldy #$10
lib_multdiv_l2
asl lib_multdiv_acc
rol lib_multdiv_acc+1
rol
rol lib_multdiv_ext+1
pha
cmp lib_multdiv_aux
lda lib_multdiv_ext+1
sbc lib_multdiv_aux+1
bcc lib_multdiv_div2
sta lib_multdiv_ext+1
pla
sbc lib_multdiv_aux
pha
inc lib_multdiv_acc
lib_multdiv_div2
pla
dey
bne lib_multdiv_l2
sta lib_multdiv_ext
ENDASM
FEND
LABEL lib_multdiv_skip
#IFEND

397
lib/string.c65 Normal file
View file

@ -0,0 +1,397 @@
//------------------------------------------------------------------------
// String library
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU GPL 2
// Language: 65CM v0.4+
// Dependencies:
// ZP usage: $fb-$fe
//
// External functions:
// lib_string_strlen
// lib_string_strchr
// lib_string_strcpy
// lib_string_stpcpy
// lib_string_strncpy
// lib_string_strcmp
// lib_string_memset
// lib_string_strcat
// lib_string_strncat
//
// Internal function:
//
//
// Note: Several of the lib-functions are combined to save space, where
// this can be done at a fairly small speed penalty.
//
//------------------------------------------------------------------------
#IFNDEF __LIB_STRING
#DEFINE __LIB_STRING = 1
GOTO lib_string_skip
//Makes it possible for all modules that needs NULL
//to define the constant
#IFNDEF NULL
#DEFINE NULL = 0
#IFEND
WORD lib_string_strptr1 @ $fb
WORD lib_string_strptr2 @ $fd
WORD lib_string_length
//just aliases for the mem* functions
WORD lib_string_ptr1 @ $fb
WORD lib_string_ptr2 @ $fd
WORD lib_string_temp
//-----------------------------------------------------------
// lib_string_strlen
//
// Purpose: Returns the string length of a null-terminated
// string in memory.
//
// Params:
// lib_string_strptr1 - pointer to string to measure
// lib_string_length - returns the length of the string
//
// lib_string_strchr
//
// Purpose: Returns pointer to where char lib_string_sl_matchchar
// first occurs in string at lib_string_strptr1.
// Returns NULL if not found.
//-----------------------------------------------------------
BYTE lib_string_sl_matchchar
FUNC lib_string_strlen ( lib_string_strptr1 out:lib_string_length )
LET lib_string_sl_matchchar = 0
FUNC lib_string_strchr ( io:lib_string_strptr1 lib_string_sl_matchchar )
BYTE char
LET lib_string_length = 0
PEEK lib_string_strptr1 -> char
WHILE char
IF char == lib_string_sl_matchchar
SUBEND
ENDIF
INC lib_string_strptr1
INC lib_string_length
PEEK lib_string_strptr1 -> char
WEND
LET lib_string_strptr1 = NULL
FEND
//-----------------------------------------------------------
// lib_string_strcpy
//
// Purpose: Copies the null-terminated string at
// lib_string_strptr1 to lib_string_strptr2
//
// lib_string_stpcpy
//
// Purpose: Same as strcpy but returns a pointer to the
// end of the copy in lib_string_strptr2
//
// lib_string_strncpy
//
// Purpose: Copies the null-terminated string at
// lib_string_strptr1 to lib_string_strptr2, but
// stops after lib_string_length chars regardless
// of if the full string is copied
//
// Note: Dont send in 0 (zero) into lib_string_length.
//-----------------------------------------------------------
FUNC lib_string_stpcpy ( lib_string_strptr1 io:lib_string_strptr2 )
FUNC lib_string_strcpy ( lib_string_strptr1 lib_string_strptr2 )
LET lib_string_length = 0
FUNC lib_string_strncpy ( lib_string_strptr1 lib_string_strptr2 lib_string_length )
BYTE char
LET char = $ff
WHILE char
PEEK lib_string_strptr1 -> char
POKE lib_string_strptr2 , char
INC lib_string_strptr1
INC lib_string_strptr2
DEC lib_string_length
IF lib_string_length == 0
BREAK
ENDIF
WEND
FEND
//-----------------------------------------------------------
// lib_string_memset
//
// Purpose: Sets lib_string_length bytes starting at
// lib_string_ptr1 to the value lib_string_memset_value
//
//-----------------------------------------------------------
BYTE lib_string_memset_value
FUNC lib_string_memset ( lib_string_ptr1 lib_string_memset_value lib_string_length )
ADD lib_string_ptr1 + lib_string_length -> lib_string_ptr2
DEC lib_string_ptr2
WHILE lib_string_ptr2 >= lib_string_ptr1
POKE lib_string_ptr2 , lib_string_memset_value
DEC lib_string_ptr2
WEND
FEND
//-----------------------------------------------------------
// lib_string_strcmp
//
// purpose: Compare two strings.
// Returns 2 if str1 is less than str2
// 1 if str1 is greater than str2
// 0 if they are equal.
//-----------------------------------------------------------
BYTE lib_string_strcmp_result
FUNC lib_string_strcmp ( lib_string_strptr1 lib_string_strptr2 out:lib_string_strcmp_result )
BYTE val1
BYTE val2
LET lib_string_strcmp_result = 0
PEEK lib_string_strptr1 -> val1
PEEK lib_string_strptr2 -> val2
WHILE val1 == val2
INC lib_string_strptr1
INC lib_string_strptr2
PEEK lib_string_strptr1 -> val1
PEEK lib_string_strptr2 -> val2
IF val1 == 0
BREAK
ENDIF
IF val2 == 0
BREAK
ENDIF
WEND
IF val1 < val2
LET lib_string_strcmp_result = 2
ENDIF
IF val1 > val2
LET lib_string_strcmp_result = 1
ENDIF
FEND
//-----------------------------------------------------------
// lib_string_strstr
//
// purpose:
// Locate the position of one string in another.
// Returns a pointer to the location of the
// substring. (or NULL if not found)
//-----------------------------------------------------------
WORD lib_string_strstr_return
FUNC lib_string_strstr ( lib_string_strptr1 lib_string_strptr2 out:lib_string_strstr_return )
LET lib_string_strstr_return = NULL
WORD strlen1
WORD strlen2
WORD substartpos
WORD mainlastpos
BYTE mainval
BYTE subval
WORD strptr1firstmatch
BYTE innerloop
//Ugly hack: Since "lib_string_strptr1" is used internally in strlen we remember the value in a temp variable
LET lib_string_temp = lib_string_strptr1
CALL lib_string_strlen ( lib_string_strptr1 strlen1 )
CALL lib_string_strlen ( lib_string_strptr2 strlen2 )
//Ugly hack: Since "lib_string_strptr1" is used internally in strlen we restore the value from a temp variable
LET lib_string_strptr1 = lib_string_temp
IF strlen1 == 0
SUBEND
ENDIF
IF strlen2 == 0
SUBEND
ENDIF
IF strlen2 > strlen1
SUBEND
ENDIF
SUBT strlen1 - strlen2 -> mainlastpos
ADD lib_string_strptr1 + mainlastpos -> mainlastpos
LET substartpos = lib_string_strptr2
LET innerloop = 0
PEEK lib_string_strptr2 -> subval
WHILE lib_string_strptr1 <= mainlastpos
PEEK lib_string_strptr1 -> mainval
IF mainval = subval
LET strptr1firstmatch = lib_string_strptr1
LET innerloop = 1
ENDIF
WHILE mainval = subval
INC lib_string_strptr1
INC lib_string_strptr2
PEEK lib_string_strptr1 -> mainval
PEEK lib_string_strptr2 -> subval
IF subval = 0
//This is the match! We found substr!
LET lib_string_strstr_return = strptr1firstmatch
SUBEND
ENDIF
WEND
IF innerloop != 0
LET innerloop = 0
LET lib_string_strptr1 = strptr1firstmatch
LET lib_string_strptr2 = substartpos
PEEK lib_string_strptr2 -> subval
ENDIF
INC lib_string_strptr1
WEND
FEND
//-----------------------------------------------------------
// lib_string_strcat
//
// purpose:
// To concatenate two strings.
// Appends lib_string_strptr1 to the
// end of lib_string_strptr2 (overwritng
// the terminating NULL character).
//
// lib_string_strncat
//
// purpose:
// very similar to strcat except at most
// lib_string_sc_copymaxchars are
// copied from the source. result is
// always NULL-terminated.
// (i.e. result string is at most
// length( lib_string_strptr1 ) +
// lib_string_sc_copymaxchars + 1
//
// Note:
// Be sure that memory after NULL
// in lib_string_strptr2 is avaliable
// for writing (so you don't overwrite
// something else lying there, or you
// may get some very unexpected
// results.
//-----------------------------------------------------------
WORD lib_string_sc_copymaxchars
FUNC lib_string_strcat ( lib_string_strptr1 lib_string_strptr2 )
LET lib_string_sc_copymaxchars = $ffff
FUNC lib_string_strncat ( lib_string_strptr1 lib_string_strptr2 lib_string_sc_copymaxchars )
BYTE char
//first find end of string2
PEEK lib_string_strptr2 -> char
WHILE char
INC lib_string_strptr2
PEEK lib_string_strptr2 -> char
WEND
PEEK lib_string_strptr1 -> char
POKE lib_string_strptr2 , char
WHILE char
PEEK lib_string_strptr1 -> char
POKE lib_string_strptr2 , char
INC lib_string_strptr1
INC lib_string_strptr2
DEC lib_string_sc_copymaxchars
IF lib_string_sc_copymaxchars == 0
POKE lib_string_strptr2 , 0
SUBEND
ENDIF
WEND
FEND
//-----------------------------------------------------------
// lib_string_memcmp
//
// Purpose:
// Compare two memory areas.
//
//-----------------------------------------------------------
BYTE lib_string_mc_res
FUNC lib_string_memcmp ( lib_string_ptr1 lib_string_ptr2 lib_string_length out:lib_string_mc_res )
BYTE b1
BYTE b2
LET lib_string_mc_res = 1
WHILE lib_string_length
PEEK lib_string_ptr1 -> b1
PEEK lib_string_ptr2 -> b2
IF b1 != b2
SUBEND
ENDIF
INC lib_string_ptr1
INC lib_string_ptr2
DEC lib_string_length
WEND
LET lib_string_mc_res = 0
FEND
//-----------------------------------------------------------
// lib_string_memcpy
//
// Purpose:
// Copy contants of a memory area to another memory area
//-----------------------------------------------------------
FUNC lib_string_memcpy ( lib_string_ptr1 lib_string_ptr2 lib_string_length )
BYTE btemp
WHILE lib_string_length
PEEK lib_string_ptr1 -> btemp
POKE lib_string_ptr2 , btemp
INC lib_string_ptr1
INC lib_string_ptr2
DEC lib_string_length
WEND
FEND
LABEL lib_string_skip
#IFEND

153
lib/strlist.c65 Normal file
View file

@ -0,0 +1,153 @@
//------------------------------------------------------------------------
// Library String-List
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies: string.c65 memlib.c65
// Target: generic 6502
//
// Purpose: Provide some functions the help to create, update and access
// a linked list of strings.
//------------------------------------------------------------------------
#IFNDEF __LIB_STRLIST
#DEFINE __LIB_STRLIST = 1
GOTO lib_strlist_skip
#INCLUDE <string.c65>
#INCLUDE <memlib.c65>
//-----------------------------------------------------------
// lib_strlist_get
//
// purpose:
// To get address to an item# (0..count-1)
// (send in item# $ffff for last item)
//
// lib_strlist_strget
//
// purpose:
// To get address of an string inside item#
// (send in item# $ffff for last item)
//
// lib_strlist_count
// purpose:
// To get the # of items in the list.
// NOTE: Returns the value in computer notation. I.e.
// 1 string, count == 0; 2 strings, count == 1 etc.
//-----------------------------------------------------------
FUNC lib_strlist_count ( {WORD objref} out:{WORD count} )
WORD itemnr
LET itemnr = $ffff
FUNC lib_strlist_get ( objref itemnr out:{WORD itemptr} )
FUNC lib_strlist_strget ( objref itemnr out:{WORD s} )
WORD nextptr
IF objref == NULL //no list, no action
EXIT
ENDIF
//return values reset
LET count = 0
LET itemptr = objref
LET s = NULL
WHILE 1
ADD 2 + itemptr -> s
GETASWORD@ itemptr -> nextptr
IF nextptr == NULL //are we at last item?
EXIT
ENDIF
IF itemnr == count
EXIT //if searching for this item we're done.
ENDIF
INC count
LET itemptr = nextptr
//MH TAG 20051218 debug
//CALL lib_cbmio_hexoutw ( itemptr )
WEND
FEND
//-----------------------------------------------------------
// lib_strlist_add
//
// purpose:
// To either create a new linklist (send in NULL as
// root-node), or add a new node to an existing linklist.
//
// Params:
// objref - send in root-node-pointer to the
// list. If new list is to be created
// send in the value NULL.
// returns: address to the root node
// on successful add, or NULL on error
// (normal cause: out of memory)
//-----------------------------------------------------------
FUNC lib_strlist_add ( io:{WORD objref} {WORD s} )
WORD link_length
WORD link_ptr
WORD old_link_ptr
IF objref != 0 //add new link
CALL lib_strlist_get ( objref $ffff old_link_ptr )
ENDIF
CALL lib_string_strlen ( s link_length )
//Add space for:
// 1. "link to next member" (2)
// 2. null terminator (1)
ADD 3 + link_length -> link_length
CALL lib_mem_malloc ( link_length link_ptr )
IF link_ptr == NULL // Alloc failed == out of memory
LET objref = NULL
EXIT
ENDIF
IF objref == 0 //create new list
LET objref = link_ptr //return the start of the list to the caller
ELSE //link up the new link to the end of the old list
PUTASWORD@ old_link_ptr , link_ptr
ENDIF
PUTASWORD@ link_ptr , NULL //Mark as last link
//move beyond link-pointer
INC link_ptr
INC link_ptr
//and finally add the string to memory
CALL lib_string_strcpy ( s link_ptr )
FEND
//-----------------------------------------------------------
// lib_strlist_free
//
// purpose:
// To remove a strlist and free all resources allocated by
// it.
//
// Params:
// lib_strlist_f_rootptr - Address to root item.
//-----------------------------------------------------------
FUNC lib_strlist_free ( io:{WORD objref} )
WORD nextptr
WHILE objref
GETASWORD@ objref -> nextptr
CALL lib_mem_free ( objref )
LET objref = nextptr
WEND
LET objref = NULL
FEND
LABEL lib_strlist_skip
#IFEND

101
lib/strlist2.c65 Normal file
View file

@ -0,0 +1,101 @@
//------------------------------------------------------------------------
// Library Extended String-List routines
//
// Author: Mattias Hansson
// Copyright (c) : 2005 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies: string.c65 memlib.c65
// Target: generic 6502
//
// Purpose: Provide more advanced functions for manipulating/maintaining
// stringlists. By making a additional library, developers can
// choose how much they wsnt compared to the memory size cost
// of the libraries.
//------------------------------------------------------------------------
#IFNDEF __LIB_STRLIST2
#DEFINE __LIB_STRLIST2 = 1
GOTO lib_strlist2_skip
#INCLUDE <strlist.c65>
//-----------------------------------------------------------
// lib_str_to_strlist
//
// purpose:
// To splitt a string into 0..several elements into a new
// stringlist. The separator character defines the
// "splitt indicator".
//
// Note: If several separator-chars are found together they
// are still threated as one.
//
//-----------------------------------------------------------
FUNC lib_strlist2_str_to_strlist ( {WORD s} {BYTE separator} io:{WORD slref} )
BYTE b
BYTE inword
WORD start
WORD copylen
WORD tmpstr_ptr
BYTE eow //end of word
LET inword = 0
LET eow = 0
WHILE 1
PEEK s -> b
IF inword == 0
IF b == 0
EXIT
ENDIF
IF b != separator //start of new "word"
LET inword = 1
LET start = s
ENDIF
ELSE //we are inside a "word"
IF b == 0
LET eow = 1
ENDIF
IF b == separator //end of this "word"
LET eow = 1
ENDIF
IF eow == 1
//copy the "word" to a new string
SUBT s - start -> copylen
//hexoutw ( copylen )
INC copylen //make room for zero termination
lib_mem_malloc ( copylen tmpstr_ptr )
IF tmpstr_ptr == NULL
EXIT //if errorhandling is to be added in the future, check this :-)
ENDIF
DEC copylen
lib_string_strncpy ( start tmpstr_ptr copylen )
//manually zero terminating the copy to add (borrowing start as temp)
ADD tmpstr_ptr + copylen -> start
POKE start , NULL
lib_strlist_add ( slref tmpstr_ptr )
lib_mem_free ( tmpstr_ptr )
LET inword = 0
LET eow = 0
ENDIF
ENDIF
IF b == 0
EXIT
ENDIF
INC s
WEND
FEND
LABEL lib_strlist2_skip
#IFEND