diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0556696 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Output directory +out diff --git a/Makefile b/Makefile index 7d7d26a..7c6b891 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ LDFLAGS = --config apple2-asm.cfg OUTDIR = out -TARGETS = $(OUTDIR)/chtype.BIN +TARGETS = $(OUTDIR)/chtype.BIN $(OUTDIR)/chtime.BIN .PHONY: clean all all: $(OUTDIR) $(TARGETS) diff --git a/chtime.s b/chtime.s new file mode 100644 index 0000000..e525bc2 --- /dev/null +++ b/chtime.s @@ -0,0 +1,292 @@ +;;; ============================================================ +;;; +;;; CHTIME - File modification time changing command for ProDOS-8 +;;; +;;; Install: +;;; -CHTIME (from BASIC.SYSTEM prompt) +;;; Usage: +;;; CHTIME filename[,Ttype][,Aaux][,S#][,D#] +;;; +;;; * filename can be relative or absolute path +;;; * specify A$nnnn to set file date +;;; * specify B$nnnn to set file time +;;; * with neither A nor B option, prints current values +;;; +;;; Build with: ca65 - https://cc65.github.io/doc/ca65.html +;;; +;;; ============================================================ + + .org $2000 + +;;; ============================================================ + +INBUF := $200 ; GETLN input buffer + +;;; ============================================================ +;;; Monitor ROM routines + +CROUT := $FD8E +PRBYTE := $FDDA +COUT := $FDED + +;;; ============================================================ +;;; ProDOS MLI / Global Page + +SET_FILE_INFO = $C3 +GET_FILE_INFO = $C4 + +DATE := $BF90 + +;;; ============================================================ +;;; BASIC.SYSTEM Global Page + +EXTRNCMD := $BE06 ; External command jmp vector +ERROUT := $BE09 ; Error routine jmp vector +XTRNADDR := $BE50 ; Ext cmd implementation addr + +XLEN := $BE52 ; Length of command string minus 1 +XCNUM := $BE53 ; Command number (ext cmd = 0). + +PBITS := $BE54 ; Command parameter bits +FBITS := $BE56 ; Found parameter bits + +.enum PBitsFlags + ;; PBITS + PFIX = $80 ; Prefix needs fetching + SLOT = $40 ; No parameters to be processed + RRUN = $20 ; Command only valid during program + FNOPT = $10 ; Filename is optional + CRFLG = $08 ; CREATE allowed + T = $04 ; File type + FN2 = $02 ; Filename '2' for RENAME + FN1 = $01 ; Filename expected + + ;; PBITS+1 + AD = $80 ; Address + B = $40 ; Byte + E = $20 ; End address + L = $10 ; Length + LINE = $08 ; '@' line number + SD = $04 ; Slot and drive numbers + F = $02 ; Field + R = $01 ; Record + + ;; Setting SD in PBITS+1 enables desired automatic behavior: if + ;; a relative path is given, an appropriate prefix is computed, + ;; using S# and D# options if supplied. Without this, absolute + ;; paths must be used if no prefix is set. +.endenum + +VADDR := $BE58 ; Address parameter +VBYTE := $BE5A ; Byte parameter +VSLOT := $BE61 ; Slot parameter +VPATH1 := $BE6C ; Pathname buffer + +GOSYSTEM := $BE70 ; Use instead of MLI + +SSGINFO := $BEB4 ; Get/Set Info Parameter block +FIFILID := $BEB8 ; (set size to set=7 or get=$A) +FIAUXID := $BEB9 +FIMDATE := $BEBE +GETBUFR := $BEF5 +;;; ============================================================ + + ;; Save previous external command address + lda EXTRNCMD+1 + sta next_command + lda EXTRNCMD+2 + sta next_command+1 + + ;; Request a 1-page buffer + lda #1 + jsr GETBUFR + bcc :+ + lda #$C ; NO BUFFERS AVAILABLE + rts +: + ;; A = MSB of new page - update absolute addresses + ;; (aligned to page boundary so only MSB changes) + sta page_num1 + sta page_num2 + sta page_num3 + + ;; Install new address in external command address + sta EXTRNCMD+2 + lda #0 + sta EXTRNCMD+1 + + ;; Relocate + ldx #0 +: lda handler,x + page_num3 := *+2 + sta $2100,x ; self-modified + inx + bne :- + + ;; Complete + rts + +;;; ============================================================ +;;; Command Handler +;;; ============================================================ + + ;; Align handler to page boundary for easier + ;; relocation + .res $2100 - *, 0 + +.proc handler + + ;; Check for this command, character by character. + ldx #0 +nxtchr: lda INBUF,x + + and #$7F ; Convert to ASCII + cmp #'a' ; Convert to upper-case + bcc :+ + cmp #'z'+1 + bcs :+ + and #$DF + + page_num1 := *+2 ; address needing updating +: cmp command_string,x + bne not_ours + inx + cpx #command_length + bne nxtchr + + ;; A match - indicate end of command string for BI's parser. + lda #command_length-1 + sta XLEN + + ;; Point BI's parser at the command execution routine. + lda #execute + sta XTRNADDR+1 + + ;; Mark command as external (zero). + lda #0 + sta XCNUM + + ;; Set accepted parameter flags + + lda #PBitsFlags::FN1 ; Filename + sta PBITS + + ;; Address, Byte, Slot & Drive handling + lda #PBitsFlags::AD | PBitsFlags::B | PBitsFlags::SD + sta PBITS+1 + + clc ; Success (so far) + rts ; Return to BASIC.SYSTEM + +;;; ============================================================ + +not_ours: + sec ; Signal failure... + next_command := *+1 + jmp $ffff ; Execute next command in chain + +;;; ============================================================ + +execute: + ;; Verify required arguments + + lda FBITS + and #PBitsFlags::FN1 ; Filename? + bne :+ + lda #$10 ; SYNTAX ERROR + sec +rts1: rts +: + +;;; -------------------------------------------------- + + ;; Get the existing file info + lda #$A + sta SSGINFO + lda #GET_FILE_INFO + jsr GOSYSTEM + bcs rts1 + +;;; -------------------------------------------------- + + ;; Apply options + ldy #0 ; count number of options + + ;; Apply optional Address argument as new date + lda FBITS+1 + and #PBitsFlags::AD ; Address set? + beq :+ + iny + lda VADDR + sta FIMDATE + lda VADDR+1 + sta FIMDATE+1 +: + + ;; Apply optional Byte argument as new time + lda FBITS+1 + and #PBitsFlags::B ; Type set? + beq :+ + iny + lda VBYTE + sta FIMDATE+2 + lda VBYTE+1 + sta FIMDATE+3 +: + + ;; If no options were used, show current details instead. + cpy #0 + beq show + + ;; Set new file info + lda #$7 + sta SSGINFO + + lda #SET_FILE_INFO + jmp GOSYSTEM + +;;; -------------------------------------------------- + +show: + lda #'A'|$80 + jsr COUT + lda #'='|$80 + jsr COUT + lda #'$'|$80 + jsr COUT + lda FIMDATE+1 + jsr PRBYTE + lda FIMDATE + jsr PRBYTE + jsr CROUT + + lda #'B'|$80 + jsr COUT + lda #'='|$80 + jsr COUT + lda #'$'|$80 + jsr COUT + lda FIMDATE+3 + jsr PRBYTE + lda FIMDATE+2 + jsr PRBYTE + jsr CROUT + + clc + rts + +;;; ============================================================ +;;; Data + +command_string: + .byte "CHTIME" ; Command string + command_length = *-command_string + +.endproc + .assert .sizeof(handler) <= $100, error, "Must fit on one page" + page_num1 := handler::page_num1 + page_num2 := handler::page_num2 + next_command := handler::next_command diff --git a/package.sh b/package.sh new file mode 100755 index 0000000..98d054b --- /dev/null +++ b/package.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Use Cadius to create a disk image for distribution +# https://github.com/mach-kernel/cadius + +set -e + +PACKDIR=$(mktemp -d) +IMGFILE="out/chtype.po" +VOLNAME="chtype" + +rm -f "$IMGFILE" +cadius CREATEVOLUME "$IMGFILE" "$VOLNAME" 140KB --no-case-bits --quiet + +add_file () { + cp "$1" "$PACKDIR/$2" + cadius ADDFILE "$IMGFILE" "/$VOLNAME" "$PACKDIR/$2" --no-case-bits --quiet +} + +add_file "out/chtype.BIN" "chtype#062000" +add_file "out/chtime.BIN" "chtime#062000" + +rm -r "$PACKDIR" + +cadius CATALOG "$IMGFILE"