Added support for building targetutils.
This commit is contained in:
@@ -74,6 +74,9 @@ MKINC = $(GEOS) \
|
||||
atari \
|
||||
nes
|
||||
|
||||
TARGETUTIL = apple2 \
|
||||
geos-apple
|
||||
|
||||
GEOSDIRS = common \
|
||||
conio \
|
||||
disk \
|
||||
@@ -90,59 +93,65 @@ GEOSDIRS = common \
|
||||
ifeq ($(TARGET),apple2enh)
|
||||
OBJPFX = a2
|
||||
DRVPFX = a2e
|
||||
DIRS = apple2
|
||||
SRCDIR = apple2
|
||||
else
|
||||
DIRS = $(TARGET)
|
||||
SRCDIR = $(TARGET)
|
||||
endif
|
||||
|
||||
SRCDIRS = $(SRCDIR)
|
||||
|
||||
ifeq ($(TARGET),$(filter $(TARGET),$(CBMS)))
|
||||
DIRS += cbm
|
||||
SRCDIRS += cbm
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),$(filter $(TARGET),$(GEOS)))
|
||||
DIRS += $(addprefix $(TARGET)/, $(GEOSDIRS))
|
||||
DIRS += $(addprefix geos-common/,$(GEOSDIRS))
|
||||
SRCDIRS += $(addprefix $(TARGET)/, $(GEOSDIRS))
|
||||
SRCDIRS += $(addprefix geos-common/,$(GEOSDIRS))
|
||||
endif
|
||||
|
||||
DIRS += common \
|
||||
conio \
|
||||
dbg \
|
||||
em \
|
||||
joystick \
|
||||
mouse \
|
||||
runtime \
|
||||
serial \
|
||||
tgi \
|
||||
zlib
|
||||
SRCDIRS += common \
|
||||
conio \
|
||||
dbg \
|
||||
em \
|
||||
joystick \
|
||||
mouse \
|
||||
runtime \
|
||||
serial \
|
||||
tgi \
|
||||
zlib
|
||||
|
||||
vpath %.s $(DIRS)
|
||||
vpath %.c $(DIRS)
|
||||
vpath %.s $(SRCDIRS)
|
||||
vpath %.c $(SRCDIRS)
|
||||
|
||||
OBJS := $(patsubst %.s,%.o,$(foreach dir,$(DIRS),$(wildcard $(dir)/*.s)))
|
||||
OBJS += $(patsubst %.c,%.o,$(foreach dir,$(DIRS),$(wildcard $(dir)/*.c)))
|
||||
OBJS := $(patsubst %.s,%.o,$(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.s)))
|
||||
OBJS += $(patsubst %.c,%.o,$(foreach dir,$(SRCDIRS),$(wildcard $(dir)/*.c)))
|
||||
|
||||
OBJS := $(addprefix ../wrk/$(TARGET)/,$(sort $(notdir $(OBJS))))
|
||||
|
||||
DEPS = $(OBJS:.o=.d)
|
||||
|
||||
EXTRA_SRCPAT = $(firstword $(DIRS))/extra/%.s
|
||||
EXTRA_SRCPAT = $(SRCDIR)/extra/%.s
|
||||
EXTRA_OBJPAT = ../lib/$(TARGET)-%.o
|
||||
EXTRA_OBJS := $(patsubst $(EXTRA_SRCPAT),$(EXTRA_OBJPAT),$(wildcard $(firstword $(DIRS))/extra/*.s))
|
||||
EXTRA_OBJS := $(patsubst $(EXTRA_SRCPAT),$(EXTRA_OBJPAT),$(wildcard $(SRCDIR)/extra/*.s))
|
||||
|
||||
ZPOBJ = ../wrk/$(TARGET)/zeropage.o
|
||||
ifeq ($(TARGET),$(filter $(TARGET),$(EXTZP)))
|
||||
ZPOBJ += ../wrk/$(TARGET)/extzp.o
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),$(filter $(TARGET),$(MKINC)))
|
||||
include $(TARGET)/Makefile.inc
|
||||
ifeq ($(SRCDIR),$(filter $(SRCDIR),$(MKINC)))
|
||||
include $(SRCDIR)/Makefile.inc
|
||||
endif
|
||||
|
||||
ifeq ($(SRCDIR),$(filter $(SRCDIR),$(TARGETUTIL)))
|
||||
include $(SRCDIR)/targetutil/Makefile.inc
|
||||
endif
|
||||
|
||||
##########
|
||||
|
||||
define DRVTYPE_template
|
||||
|
||||
$1_SRCDIR = $$(firstword $$(DIRS))/$1
|
||||
$1_SRCDIR = $$(SRCDIR)/$1
|
||||
$1_OBJDIR = ../wrk/$$(TARGET)/$1
|
||||
$1_DRVDIR = ../$1
|
||||
|
||||
@@ -158,14 +167,11 @@ $1_STCS = $$(patsubst $$($1_DRVPAT),$$($1_STCPAT),$$($1_DRVS))
|
||||
|
||||
$$($1_OBJS): | $$($1_OBJDIR)
|
||||
|
||||
$$($1_OBJDIR):
|
||||
@$$(call MKDIR,$$@)
|
||||
|
||||
$$($1_DRVPAT): $$($1_OBJPAT) $$(ZPOBJ) | $$($1_DRVDIR)
|
||||
@echo $$(TARGET) - $$(@F)
|
||||
@$$(LD) -o $$@ -t module $$^
|
||||
|
||||
$$($1_DRVDIR):
|
||||
$$($1_OBJDIR) $$($1_DRVDIR):
|
||||
@$$(call MKDIR,$$@)
|
||||
|
||||
$(TARGET): $$($1_DRVS)
|
||||
@@ -191,17 +197,32 @@ CC = $(if $(wildcard ../bin/cc65*),../bin/cc65,cc65)
|
||||
CO = $(if $(wildcard ../bin/co65*),../bin/co65,co65)
|
||||
LD = $(if $(wildcard ../bin/ld65*),../bin/ld65,ld65)
|
||||
|
||||
##########
|
||||
|
||||
define ASSEMBLE_recipe
|
||||
|
||||
@echo $(TARGET) - $<
|
||||
@$(CA) -t $(TARGET) $(AFLAGS) --create-dep $(@:.o=.d) -o $@ $<
|
||||
|
||||
endef
|
||||
|
||||
##########
|
||||
|
||||
define COMPILE_recipe
|
||||
|
||||
@echo $(TARGET) - $<
|
||||
@$(CC) -t $(TARGET) $(CFLAGS) --create-dep $(@:.o=.d) -o $(@:.o=.s) $<
|
||||
@$(CA) -t $(TARGET) -o $@ $(@:.o=.s)
|
||||
|
||||
endef
|
||||
|
||||
##########
|
||||
|
||||
../wrk/$(TARGET)/%.o: %.s | ../wrk/$(TARGET)
|
||||
@echo $(TARGET) - $<
|
||||
@$(CA) -t $(TARGET) $(AFLAGS) --create-dep $(@:.o=.d) -o $@ $<
|
||||
$(ASSEMBLE_recipe)
|
||||
|
||||
../wrk/$(TARGET)/%.o: %.c | ../wrk/$(TARGET)
|
||||
@echo $(TARGET) - $<
|
||||
@$(CC) -t $(TARGET) $(CFLAGS) --create-dep $(@:.o=.d) -o $(@:.o=.s) $<
|
||||
@$(CA) -t $(TARGET) -o $@ $(@:.o=.s)
|
||||
|
||||
../wrk/$(TARGET):
|
||||
@$(call MKDIR,$@)
|
||||
$(COMPILE_recipe)
|
||||
|
||||
$(EXTRA_OBJPAT): $(EXTRA_SRCPAT) | ../lib
|
||||
@echo $(TARGET) - $<
|
||||
@@ -210,7 +231,7 @@ $(EXTRA_OBJPAT): $(EXTRA_SRCPAT) | ../lib
|
||||
../lib/$(TARGET).lib: $(OBJS) | ../lib
|
||||
$(AR) a $@ $?
|
||||
|
||||
../lib:
|
||||
../wrk/$(TARGET) ../lib ../targetutil:
|
||||
@$(call MKDIR,$@)
|
||||
|
||||
$(TARGET): $(EXTRA_OBJS) ../lib/$(TARGET).lib
|
||||
|
||||
7
libsrc/apple2/targetutil/Makefile.inc
Normal file
7
libsrc/apple2/targetutil/Makefile.inc
Normal file
@@ -0,0 +1,7 @@
|
||||
../wrk/$(TARGET)/loader.o: $(SRCDIR)/targetutil/loader.s | ../wrk/$(TARGET)
|
||||
$(ASSEMBLE_recipe)
|
||||
|
||||
../targetutil/loader.system: ../wrk/$(TARGET)/loader.o $(SRCDIR)/targetutil/loader.cfg | ../targetutil
|
||||
$(LD) -o $@ -C $(filter %.cfg,$^) $(filter-out %.cfg,$^)
|
||||
|
||||
$(TARGET): ../targetutil/loader.system
|
||||
17
libsrc/apple2/targetutil/loader.cfg
Normal file
17
libsrc/apple2/targetutil/loader.cfg
Normal file
@@ -0,0 +1,17 @@
|
||||
#################################################################################
|
||||
# #
|
||||
# LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt) #
|
||||
# #
|
||||
#################################################################################
|
||||
|
||||
MEMORY {
|
||||
MEMORY_2000: start = $2000, size = $0200, file = %O;
|
||||
MEMORY_0300: start = $0300, size = $0100;
|
||||
}
|
||||
|
||||
SEGMENTS {
|
||||
CODE_2000: load = MEMORY_2000, type = ro;
|
||||
DATA_2000: load = MEMORY_2000, type = rw;
|
||||
CODE_0300: load = MEMORY_2000, run = MEMORY_0300, type = ro, define = yes;
|
||||
DATA_0300: load = MEMORY_2000, run = MEMORY_0300, type = rw, define = yes;
|
||||
}
|
||||
BIN
libsrc/apple2/targetutil/loader.o
Normal file
BIN
libsrc/apple2/targetutil/loader.o
Normal file
Binary file not shown.
234
libsrc/apple2/targetutil/loader.s
Normal file
234
libsrc/apple2/targetutil/loader.s
Normal file
@@ -0,0 +1,234 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
; ;
|
||||
; LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt) ;
|
||||
; ;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
A1L := $3C
|
||||
A1H := $3D
|
||||
STACK := $0100
|
||||
BUF := $0200
|
||||
PATHNAME := $0280
|
||||
MLI := $BF00
|
||||
VERSION := $FBB3
|
||||
RDKEY := $FD0C
|
||||
PRBYTE := $FDDA
|
||||
COUT := $FDED
|
||||
|
||||
QUIT_CALL = $65
|
||||
GET_FILE_INFO_CALL = $C4
|
||||
OPEN_CALL = $C8
|
||||
READ_CALL = $CA
|
||||
CLOSE_CALL = $CC
|
||||
FILE_NOT_FOUND_ERR = $46
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.import __CODE_0300_SIZE__, __DATA_0300_SIZE__
|
||||
.import __CODE_0300_LOAD__, __CODE_0300_RUN__
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.segment "DATA_2000"
|
||||
|
||||
GET_FILE_INFO_PARAM:
|
||||
.byte $0A ;PARAM_COUNT
|
||||
.addr PATHNAME ;PATHNAME
|
||||
.byte $00 ;ACCESS
|
||||
.byte $00 ;FILE_TYPE
|
||||
FILE_INFO_ADDR: .word $0000 ;AUX_TYPE
|
||||
.byte $00 ;STORAGE_TYPE
|
||||
.word $0000 ;BLOCKS_USED
|
||||
.word $0000 ;MOD_DATE
|
||||
.word $0000 ;MOD_TIME
|
||||
.word $0000 ;CREATE_DATE
|
||||
.word $0000 ;CREATE_TIME
|
||||
|
||||
OPEN_PARAM:
|
||||
.byte $03 ;PARAM_COUNT
|
||||
.addr PATHNAME ;PATHNAME
|
||||
.addr MLI - 1024 ;IO_BUFFER
|
||||
OPEN_REF: .byte $00 ;REF_NUM
|
||||
|
||||
LOADING:
|
||||
.byte $0D
|
||||
.asciiz "Loading "
|
||||
|
||||
ELLIPSES:
|
||||
.byte " ...", $0D, $0D, $00
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.segment "DATA_0300"
|
||||
|
||||
READ_PARAM:
|
||||
.byte $04 ;PARAM_COUNT
|
||||
READ_REF: .byte $00 ;REF_NUM
|
||||
READ_ADDR: .addr $0000 ;DATA_BUFFER
|
||||
.word $FFFF ;REQUEST_COUNT
|
||||
.word $0000 ;TRANS_COUNT
|
||||
|
||||
CLOSE_PARAM:
|
||||
.byte $01 ;PARAM_COUNT
|
||||
CLOSE_REF: .byte $00 ;REF_NUM
|
||||
|
||||
QUIT_PARAM:
|
||||
.byte $04 ;PARAM_COUNT
|
||||
.byte $00 ;QUIT_TYPE
|
||||
.word $0000 ;RESERVED
|
||||
.byte $00 ;RESERVED
|
||||
.word $0000 ;RESERVED
|
||||
|
||||
FILE_NOT_FOUND:
|
||||
.asciiz "... File Not Found"
|
||||
|
||||
ERROR_NUMBER:
|
||||
.asciiz "... Error $"
|
||||
|
||||
PRESS_ANY_KEY:
|
||||
.asciiz " - Press Any Key "
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.segment "CODE_2000"
|
||||
|
||||
jmp :+
|
||||
.byte $EE
|
||||
.byte $EE
|
||||
.byte 65
|
||||
STARTUP:.res 65
|
||||
|
||||
; Reset stack
|
||||
: ldx #$FF
|
||||
txs
|
||||
|
||||
; Relocate CODE_0300 and DATA_0300
|
||||
ldx #<(__CODE_0300_SIZE__ + __DATA_0300_SIZE__)
|
||||
: lda __CODE_0300_LOAD__ - 1,x
|
||||
sta __CODE_0300_RUN__ - 1,x
|
||||
dex
|
||||
bne :-
|
||||
|
||||
; Remove ".SYSTEM" from pathname
|
||||
lda PATHNAME
|
||||
sec
|
||||
sbc #.strlen(".SYSTEM")
|
||||
sta PATHNAME
|
||||
|
||||
; Add trailing '\0' to pathname
|
||||
tax
|
||||
lda #$00
|
||||
sta PATHNAME + 1,x
|
||||
|
||||
; Copy ProDOS startup filename and trailing '\0' to stack
|
||||
ldx STARTUP
|
||||
lda #$00
|
||||
beq :++ ; bra
|
||||
: lda STARTUP + 1,x
|
||||
: sta STACK,x
|
||||
dex
|
||||
bpl :--
|
||||
|
||||
; Provide some user feedback
|
||||
lda #<LOADING
|
||||
ldx #>LOADING
|
||||
jsr PRINT
|
||||
lda #<(PATHNAME + 1)
|
||||
ldx #>(PATHNAME + 1)
|
||||
jsr PRINT
|
||||
lda #<ELLIPSES
|
||||
ldx #>ELLIPSES
|
||||
jsr PRINT
|
||||
|
||||
jsr MLI
|
||||
.byte GET_FILE_INFO_CALL
|
||||
.word GET_FILE_INFO_PARAM
|
||||
bcc :+
|
||||
jmp ERROR
|
||||
|
||||
: jsr MLI
|
||||
.byte OPEN_CALL
|
||||
.word OPEN_PARAM
|
||||
bcc :+
|
||||
jmp ERROR
|
||||
|
||||
; Copy file reference number
|
||||
: lda OPEN_REF
|
||||
sta READ_REF
|
||||
sta CLOSE_REF
|
||||
|
||||
; Get load address from aux-type
|
||||
lda FILE_INFO_ADDR
|
||||
ldx FILE_INFO_ADDR + 1
|
||||
sta READ_ADDR
|
||||
stx READ_ADDR + 1
|
||||
|
||||
; It's high time to leave this place
|
||||
jmp __CODE_0300_RUN__
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
.segment "CODE_0300"
|
||||
|
||||
jsr MLI
|
||||
.byte READ_CALL
|
||||
.word READ_PARAM
|
||||
bcs ERROR
|
||||
|
||||
jsr MLI
|
||||
.byte CLOSE_CALL
|
||||
.word CLOSE_PARAM
|
||||
bcs ERROR
|
||||
|
||||
; Copy REM token and startup filename to BASIC input buffer
|
||||
ldx #$00
|
||||
lda #$B2
|
||||
bne :++ ; bra
|
||||
: inx
|
||||
lda a:STACK - 1,x
|
||||
: sta BUF,x
|
||||
bne :--
|
||||
|
||||
; Go for it ...
|
||||
jmp (READ_ADDR)
|
||||
|
||||
PRINT:
|
||||
sta A1L
|
||||
stx A1H
|
||||
ldx VERSION
|
||||
ldy #$00
|
||||
: lda (A1L),y
|
||||
beq :++
|
||||
cpx #$06 ; //e ?
|
||||
beq :+
|
||||
cmp #$60 ; lowercase ?
|
||||
bcc :+
|
||||
and #$5F ; -> uppercase
|
||||
: ora #$80
|
||||
jsr COUT
|
||||
iny
|
||||
bne :-- ; bra
|
||||
: rts
|
||||
|
||||
ERROR:
|
||||
cmp #FILE_NOT_FOUND_ERR
|
||||
bne :+
|
||||
lda #<FILE_NOT_FOUND
|
||||
ldx #>FILE_NOT_FOUND
|
||||
jsr PRINT
|
||||
beq :++ ; bra
|
||||
: pha
|
||||
lda #<ERROR_NUMBER
|
||||
ldx #>ERROR_NUMBER
|
||||
jsr PRINT
|
||||
pla
|
||||
jsr PRBYTE
|
||||
: lda #<PRESS_ANY_KEY
|
||||
ldx #>PRESS_ANY_KEY
|
||||
jsr PRINT
|
||||
jsr RDKEY
|
||||
jsr MLI
|
||||
.byte QUIT_CALL
|
||||
.word QUIT_PARAM
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
80
libsrc/apple2/targetutil/loader.txt
Normal file
80
libsrc/apple2/targetutil/loader.txt
Normal file
@@ -0,0 +1,80 @@
|
||||
LOADER.SYSTEM - an Apple][ ProDOS 8 loader for cc65 programs (Oliver Schmidt)
|
||||
=============================================================================
|
||||
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
Apple][ ProDOS 8 system programs (filetype SYS) are always loaded into memory
|
||||
starting at location $2000. This poses the problem of how to make efficient
|
||||
use of the memory in the range $0800-$2000. The usual approach of relocation
|
||||
has two downsides:
|
||||
- Relocating e.g. 30 kB from $2000-$9800 to $0800-$8000 takes a considerable
|
||||
amount of time.
|
||||
- Really large programs just don't fit into memory when loaded starting at
|
||||
location $2000.
|
||||
|
||||
The relocation can be eliminated by loading the major part(s) of the program
|
||||
from disk right to the final location by a rather small system program.
|
||||
|
||||
LOADER.SYSTEM is such a small program. In fact it's so small that it fits into
|
||||
a single block in the ProDOS 8 file system making it a so-called seedling file,
|
||||
which are loaded really fast. LOADER.SYSTEM can load cc65 programs into memory
|
||||
anywhere in the range $0800-$BB00 (44,75 kB).
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Link the cc65 program to the start address $0803 (or any other address) and
|
||||
store it as binary program (filetype BIN). This is in fact no different from
|
||||
a binary program to be run by BASIC.SYSTEM's BRUN command in the usual way.
|
||||
|
||||
If however the cc65 program isn't run by BASIC.SYSTEM but is rather run by
|
||||
LOADER.SYSTEM then it behaves like a system program which means:
|
||||
- It uses memory up to the ProDOS 8 system global page located at $BF00.
|
||||
- It supports the ProDOS 8 startup file mechanism (mapped to argv[1]).
|
||||
- It quits to the ProDOS 8 dispatcher.
|
||||
|
||||
Obviously LOADER.SYSTEM has to be told which cc65 program to run. Unfortunately
|
||||
the ProDOS 8 dispatcher has no notion of system program parameters so the usual
|
||||
approach would have been to make LOADER.SYSTEM bring up yet another menu to
|
||||
select the cc65 program to run.
|
||||
|
||||
But to allow to select the cc65 program directly from the ProDOS 8 dispatcher
|
||||
anyway LOADER.SYSTEM detects the path to the cc65 program from its own path by
|
||||
just removing the '.SYSTEM' from its name. So if you want to run the cc65
|
||||
program MYPROGRAM you'll need a copy of LOADER.SYSTEM in the same directory
|
||||
being renamed to MYPROGRAM.SYSTEM.
|
||||
|
||||
This means you will end up with a copy of LOADER.SYSTEM for every cc65 program
|
||||
to be run by it. But as LOADER.SYSTEM is a ProDOS 8 seedling file using up only
|
||||
a single block in the ProDOS 8 file system this should be no issue.
|
||||
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
In case you want to build 'loader.system' from the source code yourself you can
|
||||
do so using the following commands:
|
||||
|
||||
ca65 loader.s
|
||||
ld65 -C loader.cfg -o loader.system loader.o
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The file 'loader.system' as generated by the cc65 linker with the command above
|
||||
does NOT include the 4-byte address/length header that is generated for Apple][
|
||||
programs by default. This is because ProDOS 8 system programs are always loaded
|
||||
into memory starting at location $2000.
|
||||
|
||||
The recommended way to transfer 'loader.system' from your native file system to
|
||||
a ProDOS 8 file system disk image is to use AppleCommander which is available at
|
||||
http://applecommander.sourceforge.net/
|
||||
|
||||
If you want to put the file 'loader.system' onto a disk image 'mydisk.dsk' as
|
||||
system program MYPROGRAM.SYSTEM you can do so using the following command:
|
||||
|
||||
java -jar ac.jar -p mydisk.dsk MYPROGRAM.SYSTEM sys < loader.system
|
||||
9
libsrc/geos-apple/targetutil/Makefile.inc
Normal file
9
libsrc/geos-apple/targetutil/Makefile.inc
Normal file
@@ -0,0 +1,9 @@
|
||||
../wrk/$(TARGET)/convert.o: TARGET = apple2enh
|
||||
|
||||
../wrk/$(TARGET)/convert.o: $(SRCDIR)/targetutil/convert.c | ../wrk/$(TARGET)
|
||||
$(COMPILE_recipe)
|
||||
|
||||
../targetutil/convert.system: ../wrk/$(TARGET)/convert.o | ../targetutil
|
||||
$(LD) -o $@ -C apple2enh-system.cfg $^ apple2enh.lib
|
||||
|
||||
$(TARGET): ../targetutil/convert.system
|
||||
346
libsrc/geos-apple/targetutil/convert.c
Normal file
346
libsrc/geos-apple/targetutil/convert.c
Normal file
@@ -0,0 +1,346 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <device.h>
|
||||
#include <dio.h>
|
||||
|
||||
unsigned char info_signature[3] = {3, 21, 63 | 0x80};
|
||||
|
||||
dhandle_t dhandle;
|
||||
|
||||
struct dir_entry_t {
|
||||
struct {
|
||||
unsigned name_length :4;
|
||||
unsigned storage_type :4;
|
||||
} storage_length;
|
||||
char file_name[15];
|
||||
unsigned char file_type;
|
||||
unsigned key_pointer;
|
||||
unsigned blocks_used;
|
||||
unsigned char size[3];
|
||||
unsigned long creation;
|
||||
unsigned char version;
|
||||
unsigned char min_version;
|
||||
unsigned char access;
|
||||
unsigned aux_type;
|
||||
unsigned long last_mod;
|
||||
unsigned header_pointer;
|
||||
}* dir_entry;
|
||||
|
||||
union {
|
||||
unsigned char bytes[512];
|
||||
struct {
|
||||
unsigned prev_block;
|
||||
unsigned next_block;
|
||||
unsigned char entries[1];
|
||||
} content;
|
||||
} dir_block;
|
||||
|
||||
union {
|
||||
unsigned char bytes[512];
|
||||
struct {
|
||||
unsigned char addr_lo[254];
|
||||
unsigned char size_lo[2];
|
||||
unsigned char addr_hi[254];
|
||||
unsigned char size_hi[2];
|
||||
} content;
|
||||
} index_block, master_block, vlir_block;
|
||||
|
||||
union {
|
||||
unsigned char bytes[512];
|
||||
struct {
|
||||
unsigned reserved;
|
||||
unsigned char info_block[254];
|
||||
unsigned char vlir_records[128];
|
||||
struct dir_entry_t dir_entry;
|
||||
} content;
|
||||
} header_block;
|
||||
|
||||
|
||||
static void err_exit(char *operation, unsigned char oserr)
|
||||
{
|
||||
if (oserr) {
|
||||
fprintf(stderr, "%s - err:%02x - %s",
|
||||
operation, (int)_oserror, _stroserror(_oserror));
|
||||
} else {
|
||||
fprintf(stderr, "%s",
|
||||
operation);
|
||||
}
|
||||
getchar();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
|
||||
static unsigned get_dir_entry(char* p_name)
|
||||
{
|
||||
char* d_name;
|
||||
char* f_name;
|
||||
size_t f_namelen;
|
||||
DIR* dir;
|
||||
struct dirent* dirent;
|
||||
unsigned cur_addr;
|
||||
unsigned char entry_length;
|
||||
unsigned char entries_per_block;
|
||||
unsigned char cur_entry;
|
||||
|
||||
/* Split path name into directory name and file name */
|
||||
f_name = strrchr(p_name, '/');
|
||||
if (f_name) {
|
||||
d_name = p_name;
|
||||
*f_name++ = '\0';
|
||||
} else {
|
||||
d_name = ".";
|
||||
f_name = p_name;
|
||||
}
|
||||
f_namelen = strlen(f_name);
|
||||
|
||||
/* Start with high level functions to get handling
|
||||
of relative path and current drive for free */
|
||||
dir = opendir(d_name);
|
||||
if (!dir) {
|
||||
err_exit("opendir", 1);
|
||||
}
|
||||
dirent = readdir(dir);
|
||||
if (!dirent) {
|
||||
err_exit("readdir", 1);
|
||||
}
|
||||
|
||||
/* Field header_pointer directly follows field last_mod */
|
||||
cur_addr = *(unsigned*)(&dirent->d_mtime.hour + 1);
|
||||
|
||||
dhandle = dio_open(getcurrentdevice());
|
||||
if (!dhandle) {
|
||||
err_exit("dio_open", 1);
|
||||
}
|
||||
|
||||
if (dio_read(dhandle, cur_addr, &dir_block)) {
|
||||
err_exit("dio_read.1", 1);
|
||||
}
|
||||
|
||||
/* Get directory entry infos from directory header */
|
||||
entry_length = dir_block.bytes[0x23];
|
||||
entries_per_block = dir_block.bytes[0x24];
|
||||
|
||||
/* Skip directory header entry */
|
||||
cur_entry = 1;
|
||||
|
||||
do {
|
||||
|
||||
/* Search for next active directory entry */
|
||||
do {
|
||||
|
||||
/* Check if next directory block is necessary */
|
||||
if (cur_entry == entries_per_block) {
|
||||
|
||||
/* Check if another directory block is present */
|
||||
cur_addr = dir_block.content.next_block;
|
||||
if (!cur_addr) {
|
||||
_mappederrno(0x46);
|
||||
err_exit("dio_read.2", 1);
|
||||
}
|
||||
|
||||
/* Read next directory block */
|
||||
if (dio_read(dhandle, cur_addr, &dir_block)) {
|
||||
err_exit("dio_read.3", 1);
|
||||
}
|
||||
|
||||
/* Start with first entry in next block */
|
||||
cur_entry = 0;
|
||||
}
|
||||
|
||||
/* Compute pointer to current entry */
|
||||
dir_entry = (struct dir_entry_t*)(dir_block.content.entries +
|
||||
cur_entry * entry_length);
|
||||
|
||||
/* Switch to next entry */
|
||||
++cur_entry;
|
||||
} while (!dir_entry->storage_length.storage_type);
|
||||
|
||||
} while (dir_entry->storage_length.name_length != f_namelen ||
|
||||
strncasecmp(dir_entry->file_name, f_name, f_namelen));
|
||||
|
||||
return cur_addr;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char input[80];
|
||||
char* p_name;
|
||||
unsigned dir_addr;
|
||||
unsigned header_addr;
|
||||
unsigned char index;
|
||||
unsigned long size;
|
||||
|
||||
if (argc > 1) {
|
||||
p_name = argv[1];
|
||||
} else {
|
||||
printf("\n"
|
||||
"Apple GEOS Convert 1.0\n"
|
||||
"----------------------\n"
|
||||
"\n"
|
||||
"Pathname:");
|
||||
p_name = gets(input);
|
||||
}
|
||||
|
||||
dir_addr = get_dir_entry(p_name);
|
||||
|
||||
/* Read index block */
|
||||
if (dio_read(dhandle, dir_entry->key_pointer, &index_block)) {
|
||||
err_exit("dio_read.4", 1);
|
||||
}
|
||||
|
||||
/* First pointer is header block */
|
||||
header_addr = index_block.content.addr_lo[0] |
|
||||
index_block.content.addr_hi[0] << 8;
|
||||
|
||||
/* Read header block */
|
||||
if (dio_read(dhandle, header_addr, &header_block)) {
|
||||
err_exit("dio_read.5", 1);
|
||||
}
|
||||
|
||||
/* Do some sanity check */
|
||||
for (index = 0; index < sizeof(info_signature); ++index) {
|
||||
if (header_block.content.info_block[index] != info_signature[index]) {
|
||||
err_exit("file signature mismatch", 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check ProDOS storage type in directory entry template */
|
||||
if (header_block.content.dir_entry.storage_length.storage_type == 2)
|
||||
{
|
||||
|
||||
/* ProDOS sapling file means GEOS Sequential file*/
|
||||
printf("\nSequential file\n");
|
||||
|
||||
/* Remove header block pointer from pointer list */
|
||||
memmove(&index_block.content.addr_lo[0],
|
||||
&index_block.content.addr_lo[1], sizeof(index_block.content.addr_lo) - 1);
|
||||
memmove(&index_block.content.addr_hi[0],
|
||||
&index_block.content.addr_hi[1], sizeof(index_block.content.addr_hi) - 1);
|
||||
|
||||
/* Get file size from ProDOS directory entry template */
|
||||
size = (unsigned long)(header_block.content.dir_entry.size[0]) |
|
||||
(unsigned long)(header_block.content.dir_entry.size[1]) << 8 |
|
||||
(unsigned long)(header_block.content.dir_entry.size[2]) << 16;
|
||||
|
||||
} else {
|
||||
|
||||
/* ProDOS tree file means GEOS VLIR file */
|
||||
unsigned vlir_addr;
|
||||
unsigned long vlir_size;
|
||||
unsigned char vlir_blocks;
|
||||
unsigned char record = 0;
|
||||
|
||||
printf("\nVLIR file\n");
|
||||
|
||||
/* Skip header block pointer */
|
||||
index = 1;
|
||||
size = 0;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Get next VLIR index pointer from index block */
|
||||
vlir_addr = index_block.content.addr_lo[index] |
|
||||
index_block.content.addr_hi[index] << 8;
|
||||
++index;
|
||||
|
||||
/* Check for end of pointer list */
|
||||
if (vlir_addr == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for empty VLIRs */
|
||||
while (header_block.content.vlir_records[record] == 0xFF) {
|
||||
|
||||
/* Add empty VLIR index pointer to to master index block */
|
||||
master_block.content.addr_lo[record] = 0xFF;
|
||||
master_block.content.addr_hi[record] = 0xFF;
|
||||
++record;
|
||||
}
|
||||
|
||||
/* Add VLIR index pointer to master index block */
|
||||
master_block.content.addr_lo[record] = (unsigned char)(vlir_addr );
|
||||
master_block.content.addr_hi[record] = (unsigned char)(vlir_addr >> 8);
|
||||
++record;
|
||||
|
||||
/* Read VLIR index block */
|
||||
if (dio_read(dhandle, vlir_addr, &vlir_block)) {
|
||||
err_exit("dio_read.6", 1);
|
||||
}
|
||||
|
||||
/* Get VLIR size from VLIR index block */
|
||||
vlir_size = (unsigned long)(vlir_block.content.size_lo[1]) |
|
||||
(unsigned long)(vlir_block.content.size_hi[1]) << 8 |
|
||||
(unsigned long)(vlir_block.content.size_lo[0]) << 16 |
|
||||
(unsigned long)(vlir_block.content.size_hi[0]) << 24;
|
||||
|
||||
printf("VLIR %u size %lu bytes\n", record - 1, vlir_size);
|
||||
|
||||
/* Compute VLIR block size */
|
||||
vlir_blocks = (unsigned char)((vlir_size + 511) / 512);
|
||||
|
||||
/* Copy VLIR block pointers from index block to VLIR index block */
|
||||
memcpy(&vlir_block.content.addr_lo[0],
|
||||
&index_block.content.addr_lo[index], vlir_blocks);
|
||||
memcpy(&vlir_block.content.addr_hi[0],
|
||||
&index_block.content.addr_hi[index], vlir_blocks);
|
||||
index += vlir_blocks;
|
||||
|
||||
/* Write back VLIR index block */
|
||||
if (dio_write(dhandle, vlir_addr, &vlir_block)) {
|
||||
err_exit("dio_write.1", 1);
|
||||
}
|
||||
|
||||
/* Add VLIR size to file size */
|
||||
size += vlir_size;
|
||||
}
|
||||
|
||||
/* Replace (by now completely read) index block with
|
||||
(by now completely created) master index block */
|
||||
index_block = master_block;
|
||||
}
|
||||
|
||||
printf("File size %lu bytes\n\n", size);
|
||||
|
||||
/* Set file size in index block */
|
||||
index_block.content.size_lo[1] = (unsigned char)(size );
|
||||
index_block.content.size_hi[1] = (unsigned char)(size >> 8);
|
||||
index_block.content.size_lo[0] = (unsigned char)(size >> 16);
|
||||
index_block.content.size_hi[0] = (unsigned char)(size >> 24);
|
||||
|
||||
/* Write index block */
|
||||
if (dio_write(dhandle, dir_entry->key_pointer, &index_block)) {
|
||||
err_exit("dio_write.2", 1);
|
||||
}
|
||||
|
||||
/* Copy selected fields from directory entry template to directory block */
|
||||
dir_entry->storage_length = header_block.content.dir_entry.storage_length;
|
||||
memcpy(dir_entry->file_name, header_block.content.dir_entry.file_name, 15);
|
||||
dir_entry->file_type = header_block.content.dir_entry.file_type;
|
||||
dir_entry->size[0] = (unsigned char)(size );
|
||||
dir_entry->size[1] = (unsigned char)(size >> 8);
|
||||
dir_entry->size[2] = (unsigned char)(size >> 16);
|
||||
dir_entry->creation = header_block.content.dir_entry.creation;
|
||||
dir_entry->version = header_block.content.dir_entry.version;
|
||||
dir_entry->min_version = header_block.content.dir_entry.min_version;
|
||||
dir_entry->aux_type = header_addr;
|
||||
dir_entry->last_mod = header_block.content.dir_entry.last_mod;
|
||||
|
||||
/* Write directory block */
|
||||
if (dio_write(dhandle, dir_addr, &dir_block)) {
|
||||
err_exit("dio_write.3", 1);
|
||||
}
|
||||
|
||||
/* We're done */
|
||||
if (dio_close(dhandle)) {
|
||||
err_exit("dio_close", 1);
|
||||
}
|
||||
|
||||
printf("Convert to '%.*s' successful", dir_entry->storage_length.name_length,
|
||||
dir_entry->file_name);
|
||||
getchar();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user