aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB. Watson <urchlay@slackware.uk>2026-02-16 21:13:41 -0500
committerB. Watson <urchlay@slackware.uk>2026-02-16 21:13:41 -0500
commit5ab86143c9d0b0c86411f7067cf9649f7f0d3311 (patch)
tree44cc25726ebd6dd8c8e62876be25150a299a6c2f
parent126c147638538ce54fc21a3803955b3693a99add (diff)
downloadfujinet-chat-5ab86143c9d0b0c86411f7067cf9649f7f0d3311.tar.gz
New UI stuff, not yet working.
-rw-r--r--Makefile367
-rw-r--r--Makefile.client350
-rw-r--r--font.datbin0 -> 1024 bytes
-rw-r--r--font_dl.asm56
-rw-r--r--memsetup.asm11
-rw-r--r--src/addrs.c27
-rw-r--r--src/addrs.h15
-rw-r--r--src/edbox.c138
-rw-r--r--src/edbox.h14
-rw-r--r--src/screen.c222
-rw-r--r--src/screen.h103
-rw-r--r--uitest/test.c65
12 files changed, 1021 insertions, 347 deletions
diff --git a/Makefile b/Makefile
index febbae0..0ceb932 100644
--- a/Makefile
+++ b/Makefile
@@ -1,350 +1,23 @@
-###############################################################################
-### Generic Makefile for cc65 projects - full version with abstract options ###
-### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Łogiewa ###
-###############################################################################
-
-###############################################################################
-### In order to override defaults - values can be assigned to the variables ###
-###############################################################################
-
-# Space or comma separated list of cc65 supported target platforms to build for.
-# Default: c64 (lowercase!)
-TARGETS := atari
-
-# Name of the final, single-file executable.
-# Default: name of the current dir with target name appended
-PROGRAM := fnchat
-
-# Path(s) to additional libraries required for linking the program
-# Use only if you don't want to place copies of the libraries in SRCDIR
-# Default: none
-LIBS :=
+PARTS=memsetup.xex font_dl.xex client.xex
-# Custom linker configuration file
-# Use only if you don't want to place it in SRCDIR
-# Default: none
-CONFIG :=
-
-# Additional C compiler flags and options.
-# Default: none
-CFLAGS = -Oris
-
-# Additional assembler flags and options.
-# Default: none
-ASFLAGS =
-
-# Additional linker flags and options.
-# Default: none
-LDFLAGS = $(LDFLAGS.$(TARGETS))
-LDFLAGS.atari = --mapfile $(PROGRAM).map
-
-# Path to the directory containing C and ASM sources.
-# Default: src
-SRCDIR :=
-
-# Path to the directory where object files are to be stored (inside respective target subdirectories).
-# Default: obj
-OBJDIR :=
-
-# Command used to run the emulator.
-# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload
-EMUCMD :=
-
-# Optional commands used before starting the emulation process, and after finishing it.
-# Default: none
-#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate"
-#PREEMUCMD := osascript -e "tell application \"X11\" to activate"
-#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false"
-#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate"
-PREEMUCMD :=
-POSTEMUCMD :=
-
-# On Windows machines VICE emulators may not be available in the PATH by default.
-# In such case, please set the variable below to point to directory containing
-# VICE emulators.
-#VICE_HOME := "C:\Program Files\WinVICE-2.2-x86\"
-VICE_HOME :=
-
-# Options state file name. You should not need to change this, but for those
-# rare cases when you feel you really need to name it differently - here you are
-STATEFILE := Makefile.options
-
-###################################################################################
-#### DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! ####
-###################################################################################
-
-###################################################################################
-### Mapping abstract options to the actual compiler, assembler and linker flags ###
-### Predefined compiler, assembler and linker flags, used with abstract options ###
-### valid for 2.14.x. Consult the documentation of your cc65 version before use ###
-###################################################################################
-
-# Compiler flags used to tell the compiler to optimise for SPEED
-define _optspeed_
- CFLAGS += -Oris
-endef
-
-# Compiler flags used to tell the compiler to optimise for SIZE
-define _optsize_
- CFLAGS += -Or
-endef
-
-# Compiler and assembler flags for generating listings
-define _listing_
- CFLAGS += --listing $$(@:.o=.lst)
- ASFLAGS += --listing $$(@:.o=.lst)
- REMOVES += $(addsuffix .lst,$(basename $(OBJECTS)))
-endef
-
-# Linker flags for generating map file
-define _mapfile_
- LDFLAGS += --mapfile $$@.map
- REMOVES += $(PROGRAM).map
-endef
-
-# Linker flags for generating VICE label file
-define _labelfile_
- LDFLAGS += -Ln $$@.lbl
- REMOVES += $(PROGRAM).lbl
-endef
-
-# Linker flags for generating a debug file
-define _debugfile_
- LDFLAGS += -Wl --dbgfile,$$@.dbg
- REMOVES += $(PROGRAM).dbg
-endef
-
-###############################################################################
-### Defaults to be used if nothing defined in the editable sections above ###
-###############################################################################
-
-# Presume the C64 target like the cl65 compile & link utility does.
-# Set TARGETS to override.
-ifeq ($(TARGETS),)
- TARGETS := c64
-endif
-
-# Presume we're in a project directory so name the program like the current
-# directory. Set PROGRAM to override.
-ifeq ($(PROGRAM),)
- PROGRAM := $(notdir $(CURDIR))
-endif
-
-# Presume the C and asm source files to be located in the subdirectory 'src'.
-# Set SRCDIR to override.
-ifeq ($(SRCDIR),)
- SRCDIR := src
-endif
-
-# Presume the object and dependency files to be located in the subdirectory
-# 'obj' (which will be created). Set OBJDIR to override.
-ifeq ($(OBJDIR),)
- OBJDIR := obj
-endif
-TARGETOBJDIR := $(OBJDIR)/$(TARGETS)
-
-# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily
-# rely on cl65 being added to the PATH in this scenario.
-ifdef CC65_HOME
- CC := $(CC65_HOME)/bin/cl65
-else
- CC := cl65
-endif
-
-# Default emulator commands and options for particular targets.
-# Set EMUCMD to override.
-c64_EMUCMD := $(VICE_HOME)xscpu64 -VICIIdsize -autostart
-c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload
-vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload
-pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload
-plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload
-# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option
-c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload
-cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload
-cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload
-atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run
-
-ifeq ($(EMUCMD),)
- EMUCMD = $($(CC65TARGET)_EMUCMD)
-endif
-
-###############################################################################
-### The magic begins ###
-###############################################################################
-
-# The "Native Win32" GNU Make contains quite some workarounds to get along with
-# cmd.exe as shell. However it does not provide means to determine that it does
-# actually activate those workarounds. Especially does $(SHELL) NOT contain the
-# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to
-# execute the command 'echo' without any parameters. Only cmd.exe will return a
-# non-empy string - saying 'ECHO is on/off'.
-#
-# Many "Native Win32" prorams accept '/' as directory delimiter just fine. How-
-# ever the internal commands of cmd.exe generally require '\' to be used.
-#
-# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a
-# '-p' to create parent directories as needed.
-#
-# cmd.exe has an internal command 'del' that reports a syntax error if executed
-# without any file so make sure to call it only if there's an actual argument.
-ifeq ($(shell echo),)
- MKDIR = mkdir -p $1
- RMDIR = rmdir $1
- RMFILES = $(RM) $1
-else
- MKDIR = mkdir $(subst /,\,$1)
- RMDIR = rmdir $(subst /,\,$1)
- RMFILES = $(if $1,del /f $(subst /,\,$1))
-endif
-COMMA := ,
-SPACE := $(N/A) $(N/A)
-define NEWLINE
-
-
-endef
-# Note: Do not remove any of the two empty lines above !
-
-TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS))
-
-ifeq ($(words $(TARGETLIST)),1)
-
-# Set PROGRAM to something like 'myprog.c64'.
-override PROGRAM := $(PROGRAM).xex
-
-# Set SOURCES to something like 'src/foo.c src/bar.s'.
-# Use of assembler files with names ending differently than .s is deprecated!
-SOURCES := $(wildcard $(SRCDIR)/*.c)
-SOURCES += $(wildcard $(SRCDIR)/*.s)
-SOURCES += $(wildcard $(SRCDIR)/*.asm)
-SOURCES += $(wildcard $(SRCDIR)/*.a65)
-
-# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'.
-# Use of assembler files with names ending differently than .s is deprecated!
-SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c)
-SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s)
-SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm)
-SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65)
-
-# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'.
-OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES)))))
-
-# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'.
-DEPENDS := $(OBJECTS:.o=.d)
-
-# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'.
-LIBS += $(wildcard $(SRCDIR)/*.lib)
-LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib)
-
-# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'.
-CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg)
-CONFIG += $(wildcard $(SRCDIR)/*.cfg)
-
-# Select CONFIG file to use. Target specific configs have higher priority.
-ifneq ($(word 2,$(CONFIG)),)
- CONFIG := $(firstword $(CONFIG))
- $(info Using config file $(CONFIG) for linking)
-endif
-
-.SUFFIXES:
-.PHONY: all test clean zap love
-
-all: $(PROGRAM)
-
--include $(DEPENDS)
--include $(STATEFILE)
-
-# If OPTIONS are given on the command line then save them to STATEFILE
-# if (and only if) they have actually changed. But if OPTIONS are not
-# given on the command line then load them from STATEFILE. Have object
-# files depend on STATEFILE only if it actually exists.
-ifeq ($(origin OPTIONS),command line)
- ifneq ($(OPTIONS),$(_OPTIONS_))
- ifeq ($(OPTIONS),)
- $(info Removing OPTIONS)
- $(shell $(RM) $(STATEFILE))
- $(eval $(STATEFILE):)
- else
- $(info Saving OPTIONS=$(OPTIONS))
- $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE))
- endif
- $(eval $(OBJECTS): $(STATEFILE))
- endif
-else
- ifeq ($(origin _OPTIONS_),file)
- $(info Using saved OPTIONS=$(_OPTIONS_))
- OPTIONS = $(_OPTIONS_)
- $(eval $(OBJECTS): $(STATEFILE))
- endif
-endif
-
-# Transform the abstract OPTIONS to the actual cc65 options.
-$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_)))
-
-# Strip potential variant suffix from the actual cc65 target.
-CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST)))
-
-# The remaining targets.
-$(TARGETOBJDIR):
- $(call MKDIR,$@)
-
-vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
-
-$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR)
- $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $<
-
-vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
-
-$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR)
- $(CC) -t $(CC65TARGET) -Wa -DDYN_DRV=0 -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
-
-vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
-
-$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR)
- $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
-
-vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
-
-$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR)
- $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
-
-$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS)
- $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^)
+# remove the -a if using older versions of atasm
+ATASM=atasm -s -a
-
-test: $(PROGRAM)
- $(PREEMUCMD)
- $(EMUCMD) $<
- $(POSTEMUCMD)
-
-clean:
- $(call RMFILES,$(OBJECTS))
- $(call RMFILES,$(DEPENDS))
- $(call RMFILES,$(REMOVES))
- $(call RMFILES,$(PROGRAM))
- $(call RMFILES,test.map)
- $(call RMFILES,$(PROGRAM).map)
- $(call RMFILES,test.atr)
-
-else # $(words $(TARGETLIST)),1
-
-all test clean:
- $(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
-
-endif # $(words $(TARGETLIST)),1
-
-OBJDIRLIST := $(wildcard $(OBJDIR)/*)
-
-zap:
- $(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
- $(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
- -$(call RMDIR,$(OBJDIR))
- -$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
-
-love:
- @echo "Not war, eh?"
+all: fnchat.xex
-###################################################################
-### Place your additional targets in the additional Makefiles ###
-### in the same directory - their names have to end with ".mk"! ###
-###################################################################
--include *.mk
+fnchat.xex: $(PARTS)
+ cat $(PARTS) > fnchat.xex
+
+client.xex:
+ $(MAKE) -f Makefile.client
+
+%.xex: %.asm
+ $(ATASM) -o$@ $<
+
+memsetup.xex: memsetup.asm
+
+font_dl.xex: font_dl.asm font.dat
+
+test:
+ cl65 -C src/atari.cfg -t atari -o testmain.xex src/addrs.c src/edbox.c src/screen.c uitest/test.c
+ cat memsetup.xex font_dl.xex testmain.xex > test.xex
diff --git a/Makefile.client b/Makefile.client
new file mode 100644
index 0000000..febbae0
--- /dev/null
+++ b/Makefile.client
@@ -0,0 +1,350 @@
+###############################################################################
+### Generic Makefile for cc65 projects - full version with abstract options ###
+### V1.3.0(w) 2010 - 2013 Oliver Schmidt & Patryk "Silver Dream !" Łogiewa ###
+###############################################################################
+
+###############################################################################
+### In order to override defaults - values can be assigned to the variables ###
+###############################################################################
+
+# Space or comma separated list of cc65 supported target platforms to build for.
+# Default: c64 (lowercase!)
+TARGETS := atari
+
+# Name of the final, single-file executable.
+# Default: name of the current dir with target name appended
+PROGRAM := fnchat
+
+# Path(s) to additional libraries required for linking the program
+# Use only if you don't want to place copies of the libraries in SRCDIR
+# Default: none
+LIBS :=
+
+# Custom linker configuration file
+# Use only if you don't want to place it in SRCDIR
+# Default: none
+CONFIG :=
+
+# Additional C compiler flags and options.
+# Default: none
+CFLAGS = -Oris
+
+# Additional assembler flags and options.
+# Default: none
+ASFLAGS =
+
+# Additional linker flags and options.
+# Default: none
+LDFLAGS = $(LDFLAGS.$(TARGETS))
+LDFLAGS.atari = --mapfile $(PROGRAM).map
+
+# Path to the directory containing C and ASM sources.
+# Default: src
+SRCDIR :=
+
+# Path to the directory where object files are to be stored (inside respective target subdirectories).
+# Default: obj
+OBJDIR :=
+
+# Command used to run the emulator.
+# Default: depending on target platform. For default (c64) target: x64 -kernal kernal -VICIIdsize -autoload
+EMUCMD :=
+
+# Optional commands used before starting the emulation process, and after finishing it.
+# Default: none
+#PREEMUCMD := osascript -e "tell application \"System Events\" to set isRunning to (name of processes) contains \"X11.bin\"" -e "if isRunning is true then tell application \"X11\" to activate"
+#PREEMUCMD := osascript -e "tell application \"X11\" to activate"
+#POSTEMUCMD := osascript -e "tell application \"System Events\" to tell process \"X11\" to set visible to false"
+#POSTEMUCMD := osascript -e "tell application \"Terminal\" to activate"
+PREEMUCMD :=
+POSTEMUCMD :=
+
+# On Windows machines VICE emulators may not be available in the PATH by default.
+# In such case, please set the variable below to point to directory containing
+# VICE emulators.
+#VICE_HOME := "C:\Program Files\WinVICE-2.2-x86\"
+VICE_HOME :=
+
+# Options state file name. You should not need to change this, but for those
+# rare cases when you feel you really need to name it differently - here you are
+STATEFILE := Makefile.options
+
+###################################################################################
+#### DO NOT EDIT BELOW THIS LINE, UNLESS YOU REALLY KNOW WHAT YOU ARE DOING! ####
+###################################################################################
+
+###################################################################################
+### Mapping abstract options to the actual compiler, assembler and linker flags ###
+### Predefined compiler, assembler and linker flags, used with abstract options ###
+### valid for 2.14.x. Consult the documentation of your cc65 version before use ###
+###################################################################################
+
+# Compiler flags used to tell the compiler to optimise for SPEED
+define _optspeed_
+ CFLAGS += -Oris
+endef
+
+# Compiler flags used to tell the compiler to optimise for SIZE
+define _optsize_
+ CFLAGS += -Or
+endef
+
+# Compiler and assembler flags for generating listings
+define _listing_
+ CFLAGS += --listing $$(@:.o=.lst)
+ ASFLAGS += --listing $$(@:.o=.lst)
+ REMOVES += $(addsuffix .lst,$(basename $(OBJECTS)))
+endef
+
+# Linker flags for generating map file
+define _mapfile_
+ LDFLAGS += --mapfile $$@.map
+ REMOVES += $(PROGRAM).map
+endef
+
+# Linker flags for generating VICE label file
+define _labelfile_
+ LDFLAGS += -Ln $$@.lbl
+ REMOVES += $(PROGRAM).lbl
+endef
+
+# Linker flags for generating a debug file
+define _debugfile_
+ LDFLAGS += -Wl --dbgfile,$$@.dbg
+ REMOVES += $(PROGRAM).dbg
+endef
+
+###############################################################################
+### Defaults to be used if nothing defined in the editable sections above ###
+###############################################################################
+
+# Presume the C64 target like the cl65 compile & link utility does.
+# Set TARGETS to override.
+ifeq ($(TARGETS),)
+ TARGETS := c64
+endif
+
+# Presume we're in a project directory so name the program like the current
+# directory. Set PROGRAM to override.
+ifeq ($(PROGRAM),)
+ PROGRAM := $(notdir $(CURDIR))
+endif
+
+# Presume the C and asm source files to be located in the subdirectory 'src'.
+# Set SRCDIR to override.
+ifeq ($(SRCDIR),)
+ SRCDIR := src
+endif
+
+# Presume the object and dependency files to be located in the subdirectory
+# 'obj' (which will be created). Set OBJDIR to override.
+ifeq ($(OBJDIR),)
+ OBJDIR := obj
+endif
+TARGETOBJDIR := $(OBJDIR)/$(TARGETS)
+
+# On Windows it is mandatory to have CC65_HOME set. So do not unnecessarily
+# rely on cl65 being added to the PATH in this scenario.
+ifdef CC65_HOME
+ CC := $(CC65_HOME)/bin/cl65
+else
+ CC := cl65
+endif
+
+# Default emulator commands and options for particular targets.
+# Set EMUCMD to override.
+c64_EMUCMD := $(VICE_HOME)xscpu64 -VICIIdsize -autostart
+c128_EMUCMD := $(VICE_HOME)x128 -kernal kernal -VICIIdsize -autoload
+vic20_EMUCMD := $(VICE_HOME)xvic -kernal kernal -VICdsize -autoload
+pet_EMUCMD := $(VICE_HOME)xpet -Crtcdsize -autoload
+plus4_EMUCMD := $(VICE_HOME)xplus4 -TEDdsize -autoload
+# So far there is no x16 emulator in VICE (why??) so we have to use xplus4 with -memsize option
+c16_EMUCMD := $(VICE_HOME)xplus4 -ramsize 16 -TEDdsize -autoload
+cbm510_EMUCMD := $(VICE_HOME)xcbm2 -model 510 -VICIIdsize -autoload
+cbm610_EMUCMD := $(VICE_HOME)xcbm2 -model 610 -Crtcdsize -autoload
+atari_EMUCMD := atari800 -windowed -xl -pal -nopatchall -run
+
+ifeq ($(EMUCMD),)
+ EMUCMD = $($(CC65TARGET)_EMUCMD)
+endif
+
+###############################################################################
+### The magic begins ###
+###############################################################################
+
+# The "Native Win32" GNU Make contains quite some workarounds to get along with
+# cmd.exe as shell. However it does not provide means to determine that it does
+# actually activate those workarounds. Especially does $(SHELL) NOT contain the
+# value 'cmd.exe'. So the usual way to determine if cmd.exe is being used is to
+# execute the command 'echo' without any parameters. Only cmd.exe will return a
+# non-empy string - saying 'ECHO is on/off'.
+#
+# Many "Native Win32" prorams accept '/' as directory delimiter just fine. How-
+# ever the internal commands of cmd.exe generally require '\' to be used.
+#
+# cmd.exe has an internal command 'mkdir' that doesn't understand nor require a
+# '-p' to create parent directories as needed.
+#
+# cmd.exe has an internal command 'del' that reports a syntax error if executed
+# without any file so make sure to call it only if there's an actual argument.
+ifeq ($(shell echo),)
+ MKDIR = mkdir -p $1
+ RMDIR = rmdir $1
+ RMFILES = $(RM) $1
+else
+ MKDIR = mkdir $(subst /,\,$1)
+ RMDIR = rmdir $(subst /,\,$1)
+ RMFILES = $(if $1,del /f $(subst /,\,$1))
+endif
+COMMA := ,
+SPACE := $(N/A) $(N/A)
+define NEWLINE
+
+
+endef
+# Note: Do not remove any of the two empty lines above !
+
+TARGETLIST := $(subst $(COMMA),$(SPACE),$(TARGETS))
+
+ifeq ($(words $(TARGETLIST)),1)
+
+# Set PROGRAM to something like 'myprog.c64'.
+override PROGRAM := $(PROGRAM).xex
+
+# Set SOURCES to something like 'src/foo.c src/bar.s'.
+# Use of assembler files with names ending differently than .s is deprecated!
+SOURCES := $(wildcard $(SRCDIR)/*.c)
+SOURCES += $(wildcard $(SRCDIR)/*.s)
+SOURCES += $(wildcard $(SRCDIR)/*.asm)
+SOURCES += $(wildcard $(SRCDIR)/*.a65)
+
+# Add to SOURCES something like 'src/c64/me.c src/c64/too.s'.
+# Use of assembler files with names ending differently than .s is deprecated!
+SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.c)
+SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.s)
+SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.asm)
+SOURCES += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.a65)
+
+# Set OBJECTS to something like 'obj/c64/foo.o obj/c64/bar.o'.
+OBJECTS := $(addsuffix .o,$(basename $(addprefix $(TARGETOBJDIR)/,$(notdir $(SOURCES)))))
+
+# Set DEPENDS to something like 'obj/c64/foo.d obj/c64/bar.d'.
+DEPENDS := $(OBJECTS:.o=.d)
+
+# Add to LIBS something like 'src/foo.lib src/c64/bar.lib'.
+LIBS += $(wildcard $(SRCDIR)/*.lib)
+LIBS += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.lib)
+
+# Add to CONFIG something like 'src/c64/bar.cfg src/foo.cfg'.
+CONFIG += $(wildcard $(SRCDIR)/$(TARGETLIST)/*.cfg)
+CONFIG += $(wildcard $(SRCDIR)/*.cfg)
+
+# Select CONFIG file to use. Target specific configs have higher priority.
+ifneq ($(word 2,$(CONFIG)),)
+ CONFIG := $(firstword $(CONFIG))
+ $(info Using config file $(CONFIG) for linking)
+endif
+
+.SUFFIXES:
+.PHONY: all test clean zap love
+
+all: $(PROGRAM)
+
+-include $(DEPENDS)
+-include $(STATEFILE)
+
+# If OPTIONS are given on the command line then save them to STATEFILE
+# if (and only if) they have actually changed. But if OPTIONS are not
+# given on the command line then load them from STATEFILE. Have object
+# files depend on STATEFILE only if it actually exists.
+ifeq ($(origin OPTIONS),command line)
+ ifneq ($(OPTIONS),$(_OPTIONS_))
+ ifeq ($(OPTIONS),)
+ $(info Removing OPTIONS)
+ $(shell $(RM) $(STATEFILE))
+ $(eval $(STATEFILE):)
+ else
+ $(info Saving OPTIONS=$(OPTIONS))
+ $(shell echo _OPTIONS_=$(OPTIONS) > $(STATEFILE))
+ endif
+ $(eval $(OBJECTS): $(STATEFILE))
+ endif
+else
+ ifeq ($(origin _OPTIONS_),file)
+ $(info Using saved OPTIONS=$(_OPTIONS_))
+ OPTIONS = $(_OPTIONS_)
+ $(eval $(OBJECTS): $(STATEFILE))
+ endif
+endif
+
+# Transform the abstract OPTIONS to the actual cc65 options.
+$(foreach o,$(subst $(COMMA),$(SPACE),$(OPTIONS)),$(eval $(_$o_)))
+
+# Strip potential variant suffix from the actual cc65 target.
+CC65TARGET := $(firstword $(subst .,$(SPACE),$(TARGETLIST)))
+
+# The remaining targets.
+$(TARGETOBJDIR):
+ $(call MKDIR,$@)
+
+vpath %.c $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
+
+$(TARGETOBJDIR)/%.o: %.c | $(TARGETOBJDIR)
+ $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(CFLAGS) -o $@ $<
+
+vpath %.s $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
+
+$(TARGETOBJDIR)/%.o: %.s | $(TARGETOBJDIR)
+ $(CC) -t $(CC65TARGET) -Wa -DDYN_DRV=0 -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
+
+vpath %.asm $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
+
+$(TARGETOBJDIR)/%.o: %.asm | $(TARGETOBJDIR)
+ $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
+
+vpath %.a65 $(SRCDIR)/$(TARGETLIST) $(SRCDIR)
+
+$(TARGETOBJDIR)/%.o: %.a65 | $(TARGETOBJDIR)
+ $(CC) -t $(CC65TARGET) -c --create-dep $(@:.o=.d) $(ASFLAGS) -o $@ $<
+
+$(PROGRAM): $(CONFIG) $(OBJECTS) $(LIBS)
+ $(CC) -t $(CC65TARGET) $(LDFLAGS) -o $@ $(patsubst %.cfg,-C %.cfg,$^)
+
+
+test: $(PROGRAM)
+ $(PREEMUCMD)
+ $(EMUCMD) $<
+ $(POSTEMUCMD)
+
+clean:
+ $(call RMFILES,$(OBJECTS))
+ $(call RMFILES,$(DEPENDS))
+ $(call RMFILES,$(REMOVES))
+ $(call RMFILES,$(PROGRAM))
+ $(call RMFILES,test.map)
+ $(call RMFILES,$(PROGRAM).map)
+ $(call RMFILES,test.atr)
+
+else # $(words $(TARGETLIST)),1
+
+all test clean:
+ $(foreach t,$(TARGETLIST),$(MAKE) TARGETS=$t $@$(NEWLINE))
+
+endif # $(words $(TARGETLIST)),1
+
+OBJDIRLIST := $(wildcard $(OBJDIR)/*)
+
+zap:
+ $(foreach o,$(OBJDIRLIST),-$(call RMFILES,$o/*.o $o/*.d $o/*.lst)$(NEWLINE))
+ $(foreach o,$(OBJDIRLIST),-$(call RMDIR,$o)$(NEWLINE))
+ -$(call RMDIR,$(OBJDIR))
+ -$(call RMFILES,$(basename $(PROGRAM)).* $(STATEFILE))
+
+love:
+ @echo "Not war, eh?"
+
+###################################################################
+### Place your additional targets in the additional Makefiles ###
+### in the same directory - their names have to end with ".mk"! ###
+###################################################################
+-include *.mk
diff --git a/font.dat b/font.dat
new file mode 100644
index 0000000..0527aaf
--- /dev/null
+++ b/font.dat
Binary files differ
diff --git a/font_dl.asm b/font_dl.asm
new file mode 100644
index 0000000..bbb2b98
--- /dev/null
+++ b/font_dl.asm
@@ -0,0 +1,56 @@
+ *= $a000
+ .incbin "font.dat"
+dlist
+ .byte $70, $70, $30 ; 2 8-line blanks, 1 4-line blank
+ .byte $42 ; LMS GR.0
+dl_top_lms
+ .word $a500 ; base address of 1st screen
+ .byte $02, $02, $02, $02, $02, $02, $02, $02
+ .byte $02, $02, $02, $02, $02, $02, $02, $02
+ .byte $02, $02, $02, $02, $02, $02 ; 22 GR.0 lines
+ .byte $00 ; 1 blank line
+ .byte $42 ; another LMS (edit/status box)
+dl_bottom_lms
+ .word $a480 ; edit box base address
+ .word $02 ; 2nd line of edit box
+ .byte $41 ; JVB
+ .word dlist
+
+status_box
+ *= * + 80 ; 2 lines
+
+edit_box
+ *= * + 160 ; 4 lines
+
+end_boxes ; currently $a515
+
+; screens are 920 bytes each (23 lines * 40), cannot
+; cross the 4K boundary at $b000, and must end
+; before the ROM (or unwired addresses) at $c000.
+
+; on a 52K machine (expanded 800 or XL/XE running Translator),
+; we'd have another 4K of RAM from $c000-$cfff. With Translator,
+; we also could have 2K at $d800-$dfff (the floating point ROM,
+; which we don't use)... and we wouldn't have to have the font
+; at $a000, it could be plopped right into the OS default location
+; at $e000. maybe this will happen someday. meanwhile:
+
+ *= $b000 - (920 * 3)
+screen0
+ *= * + 920
+screen1
+ *= * + 920
+screen2 ; ends 1 byte before $b000 boundary
+ *= * + 920
+screen3 ; starts at $b000 boundary
+ *= * + 920
+screen4
+ *= * + 920
+screen5
+ *= * + 920
+screen6
+ *= * + 920
+end_screens ; currently $be60
+
+; 7 screens ain't so bad, really. eventually though, we want to
+; add scrollback to them, meaning fewer of them. sigh.
diff --git a/memsetup.asm b/memsetup.asm
new file mode 100644
index 0000000..49d8149
--- /dev/null
+++ b/memsetup.asm
@@ -0,0 +1,11 @@
+ *= $2000
+memsetup
+ lda #$a0
+ sta $6a ; RAMTOP
+ sta $02e6 ; MEMTOP high
+ lda #0
+ sta $02e5 ; MEMTOP low
+ rts
+
+ *= $02e2 ; INITAD
+ .word memsetup
diff --git a/src/addrs.c b/src/addrs.c
new file mode 100644
index 0000000..8c4ad1b
--- /dev/null
+++ b/src/addrs.c
@@ -0,0 +1,27 @@
+#include "addrs.h"
+
+u8 *dlist = u8p(DLIST_ADDR);
+u16 *dlist_top_lms = u16p(0xa404);
+u16 *dlist_bottom_lms = u16p(0xa41e);
+u8 *status_box = u8p(0xa425); /* 80 bytes */
+u8 *edit_box = u8p(0xa475); /* 160 bytes */
+
+u8 *screen_addrs[7] = {
+ u8p(0xa538),
+ u8p(0xa8d0),
+ u8p(0xac68),
+ u8p(0xb000),
+ u8p(0xb398),
+ u8p(0xb730),
+ u8p(0xbac8)
+};
+
+u8 *screen_botlines[7] = {
+ u8p(0xa8a8),
+ u8p(0xac40),
+ u8p(0xafd8),
+ u8p(0xb370),
+ u8p(0xb708),
+ u8p(0xbaa0),
+ u8p(0xbe38)
+};
diff --git a/src/addrs.h b/src/addrs.h
new file mode 100644
index 0000000..6d44a68
--- /dev/null
+++ b/src/addrs.h
@@ -0,0 +1,15 @@
+#define FONT_ADDR_HI 0xa0
+#define DLIST_ADDR 0xa400
+
+#define u8 unsigned char
+#define u8p(x) ((unsigned char *)x)
+#define u16 unsigned int
+#define u16p(x) ((unsigned int *)x)
+
+extern u8 *dlist;
+extern u16 *dlist_top_lms;
+extern u16 *dlist_bottom_lms;
+extern u8 *status_box;
+extern u8 *edit_box;
+extern u8 *screen_addrs[7];
+extern u8 *screen_botlines[7];
diff --git a/src/edbox.c b/src/edbox.c
new file mode 100644
index 0000000..f1c77d1
--- /dev/null
+++ b/src/edbox.c
@@ -0,0 +1,138 @@
+#include <atari.h>
+#include <conio.h>
+#include <string.h>
+#include "addrs.h"
+#include "screen.h"
+#include "edbox.h"
+
+/* TODO: tab completion */
+
+// static int edbox_visible = 0; /* don't think we'll ever need it */
+static u16 edbox_pos; /* range 0 to EDBOX_SIZE - 1 */
+
+void (*edbox_callback)(void);
+
+static void hide_cursor(void) {
+ edit_box[edbox_pos] &= 0x7f;
+}
+
+static void show_cursor(void) {
+ edit_box[edbox_pos] |= 0x80;
+}
+
+void edbox_clear(void) {
+ memset(edit_box, 0, EDBOX_SIZE);
+ edbox_pos = 0;
+ show_cursor();
+}
+
+void edbox_show(void) {
+ u16 addr;
+
+ if(edbox_pos < 80)
+ addr = (u16)edit_box;
+ else
+ addr = (u16)edit_box + edbox_pos - 79;
+
+ scr_waitvcount(116);
+
+ *dlist_bottom_lms = addr;
+}
+
+void edbox_hide(void) {
+ scr_refresh();
+}
+
+void edbox_putc(char c) {
+ if(!c)
+ return; /* no inserting nulls */
+ if((c != CH_EOL) && (edbox_pos == EDBOX_SIZE - 1))
+ return;
+ edit_box[edbox_pos++] = c;
+ edbox_show();
+}
+
+static void special_keystroke(char c) {
+ OS.ch = 0xff;
+ edbox_putc(c);
+}
+
+static void backspace(void) {
+ if(!edbox_pos) return;
+ edit_box[--edbox_pos] = 0;
+ edbox_show();
+}
+
+static void del_word(void) {
+}
+
+static void normal_keystroke(void) {
+ char c;
+
+ c = cgetc();
+
+ switch(c) {
+ case CH_EOL:
+ edbox_putc(c);
+ if(edbox_callback)
+ (*edbox_callback)();
+ /* fall thru */
+ case CH_CLR:
+ edbox_hide();
+ /* fall thru */
+ case CH_DELLINE:
+ case 0x15: /* ^U */
+ edbox_clear();
+ break;
+ case CH_DEL:
+ backspace();
+ break;
+ case 0x17: /* ^W */
+ del_word();
+ break;
+ default:
+ edbox_putc(c);
+ break;
+ }
+}
+
+void edbox_keystroke(void) {
+ char c;
+
+ hide_cursor();
+ edbox_show();
+
+ OS.invflg = c = 0;
+
+ /* XXX: these keys don't click. */
+ switch(OS.ch) {
+ case 0xa0: /* key: ctrl [ */
+ c = 0x7b; /* ascii: { */
+ break;
+ case 0xa2: /* key: ctrl ] */
+ c = 0x7d; /* ascii: } */
+ break;
+ case 0x1c: /* key: ESC */
+ c = 0x60; /* ascii: ` */
+ break;
+ case 0x5c: /* key: shift ESC */
+ case 0x9c: /* key: ctrl ESC */
+ c = 0x7e; /* ascii: ~ */
+ break;
+ case 0x27: /* atari key */
+ case 0x67: /* ...w/shift */
+ case 0x97: /* ...w/ctrl */
+ return; /* ignore it! */
+ break;
+ default:
+ break;
+ }
+
+ if(c) {
+ special_keystroke(c);
+ } else {
+ normal_keystroke();
+ }
+
+ show_cursor();
+}
diff --git a/src/edbox.h b/src/edbox.h
new file mode 100644
index 0000000..ad70cc6
--- /dev/null
+++ b/src/edbox.h
@@ -0,0 +1,14 @@
+#include "addrs.h"
+
+/**** public API ****/
+
+#define EDBOX_SIZE 160
+
+void edbox_clear(void);
+void edbox_show(void);
+void edbox_hide(void);
+void edbox_putc(char c);
+void edbox_keystroke(void);
+
+/* called when the user presses Enter */
+extern void (*edbox_callback)(void);
diff --git a/src/screen.c b/src/screen.c
new file mode 100644
index 0000000..7f34331
--- /dev/null
+++ b/src/screen.c
@@ -0,0 +1,222 @@
+#include <atari.h>
+#include <string.h>
+
+#include "addrs.h"
+#include "screen.h"
+
+#define SDLST ((u16 *)0x0230)
+
+char scr_active[MAX_SCREENS];
+
+/* the screen that's currently displaying */
+char scr_current;
+
+static char scr_names[7][32];
+static char scr_topics[7][40];
+
+static char xpos;
+
+void scr_waitvcount(u8 c) {
+ while(ANTIC.vcount < c)
+ /* NOP */;
+}
+
+static void scr_clear(char s) {
+ memset(screen_addrs[s], 0, SCREEN_SIZE);
+ memset(scr_names[s], 0, 32);
+ memset(scr_topics[s], 0, LINE_SIZE);
+}
+
+static void scr_scroll(char s) {
+ memmove(screen_addrs[s], screen_addrs[s] + 40, 880);
+ memset(screen_botlines[s], 0, 40);
+}
+
+void scr_init(void) {
+ int i, old_dma;
+
+ old_dma = OS.sdmctl;
+ OS.sdmctl = 0;
+ scr_waitvcount(112); /* after the last GR.0 line */
+ *SDLST = DLIST_ADDR;
+ OS.chbas = FONT_ADDR_HI;
+
+ for(i = 0; i < MAX_SCREENS; i++) {
+ scr_clear(i);
+ scr_active[i] = SCR_UNUSED;
+ }
+
+ strcpy(scr_names[0], "[server]");
+ strcpy(scr_names[1], "[private]");
+ scr_active[0] = scr_active[1] = SCR_INACTIVE;
+
+ OS.sdmctl = old_dma;
+
+ scr_display(0);
+}
+
+char scr_create(const char *name, char display) {
+ int i;
+
+ for(i = 0; i < MAX_SCREENS; i++) {
+ if(scr_active[i] == SCR_UNUSED) {
+ strcpy(scr_names[i], name);
+ scr_active[i] = SCR_INACTIVE;
+ scr_topics[i][0] = '\0';
+ if(display)
+ scr_display(i);
+ else
+ scr_display(scr_current);
+ return i;
+ }
+ }
+
+ // scr_print(SCR_CURR, "Can't create window (all in use)\n");
+ return 0xff;
+}
+
+void scr_destroy(char s) {
+ /* <2 because screens 0 and 1 (server and private) cannot be destroyed */
+ if(s < 2 || s >= MAX_SCREENS)
+ return;
+
+ scr_active[s] = SCR_UNUSED;
+ if(scr_current == s)
+ scr_display(0);
+}
+
+void scr_set_topic(char s, const char *topic) {
+ strncpy(scr_topics[s], topic, 40);
+}
+
+void scr_display(char s) {
+ if(s >= MAX_SCREENS)
+ return;
+
+ /* leave this out, for testing
+ if(scr_active[s] == SCR_UNUSED)
+ return;
+ */
+
+ scr_active[s] = SCR_INACTIVE;
+ scr_current = s;
+
+ scr_waitvcount(112);
+ *dlist_top_lms = (u16)screen_addrs[s];
+
+ scr_show_status(s);
+}
+
+void scr_show_status(char s) {
+ int i;
+ char *p, sc;
+
+ status_box[0] = s + 177; /* inverse number */
+ status_box[1] = ':';
+ strncpy(status_box + 2, scr_names[s], 32);
+ strncpy(status_box + 40, scr_topics[s], 40);
+
+ p = status_box + 33;
+ for(i = 0; i < MAX_SCREENS; i++) {
+ switch(scr_active[i]) {
+ case SCR_ACTIVE:
+ sc = 128 | ('1' + i); break;
+ case SCR_INACTIVE:
+ sc = '1' + i; break;
+ default:
+ sc = ' ';
+ }
+ *p++ = sc;
+ }
+
+ scr_waitvcount(112);
+ *dlist_bottom_lms = (u16)status_box;
+}
+
+void scr_refresh(void) {
+ scr_display(scr_current);
+}
+
+char scr_getbyname(const char *name) {
+ char i;
+
+ for(i = 2; i < MAX_SCREENS; i++) {
+ if(strcasecmp(name, scr_names[i]) == 0)
+ return i;
+ }
+
+ return 0;
+}
+
+/* TODO: skip color codes (start with 0x03 or 0x04).
+ if we're going to ever support utf-8, decode it here...
+ also, 0x16 is supposed to be reverse video. not widely used/supported.
+ eventually, this will be rewritten in asm anyway. */
+void scr_putc(char s, char c) {
+ u8 *dest;
+ static char bold = 0;
+
+ if(s == SCR_CURR)
+ s = scr_current;
+
+ if(c == '\n') {
+ bold = 0;
+ if(!xpos)
+ return;
+ xpos = 40;
+ return;
+ }
+
+ if(xpos == 40) {
+ scr_scroll(s);
+ xpos = 0;
+ }
+
+ dest = screen_botlines[s] + xpos;
+
+ if(c & 0x80) {
+ /* utf-8 (or maybe latin-1), we don't support it yet */
+ c = 0xbf; /* inverse ? */
+ } else if(c == 0x02) {
+ bold = !bold;
+ return;
+ } else if(c == 0x1d) {
+ /* italics */
+ c = '/';
+ } else if(c == 0x1e) {
+ /* strikethru */
+ c = '-';
+ } else if(c == 0x1f) {
+ /* underline */
+ c = '_';
+ } else if(c == 0x0f) {
+ /* all formatting off */
+ bold = 0;
+ return;
+ } else if(c < 0x20) {
+ /* unsupported control character */
+ c += 192;
+ }
+
+ if(bold) c |= 0x80;
+
+ *dest = c;
+ xpos++;
+}
+
+void scr_print(char s, const char *text) {
+ while(*text) {
+ scr_putc(s, *text);
+ if(*text == '\n')
+ break;
+ text++;
+ }
+ scr_activate(s);
+}
+
+void scr_activate(char s) {
+ if(s != scr_current) {
+ scr_active[s] = SCR_ACTIVE;
+ scr_show_status(scr_current);
+ }
+}
diff --git a/src/screen.h b/src/screen.h
new file mode 100644
index 0000000..5762009
--- /dev/null
+++ b/src/screen.h
@@ -0,0 +1,103 @@
+#include "addrs.h"
+
+#define MAX_SCREENS 7
+#define LINE_SIZE 40
+#define SCREEN_SIZE 920
+#define BOTTOM_LINE_OFFS 880
+
+#define SCR_UNUSED 0
+#define SCR_INACTIVE 1
+#define SCR_ACTIVE 2
+
+#define SCR_CURR 0xff
+
+/**** public API ****/
+
+/* the screen that's currently displaying */
+extern char scr_current;
+
+/* which screens are "active". the screen being displayed is
+ never active. any screen that's not visible that's had anything
+ written to it gets marked active. switching the display to an
+ active screen clears its active flag. */
+extern char scr_active[MAX_SCREENS];
+
+/* call before using any of the other functions. sets up the
+ display list, clears all screen memory, selects screen 0
+ for display. */
+void scr_init(void);
+
+/* creates a screen, if possible. we only get 7; attempts to create
+ more that that will return -1. on success, returns the screen
+ number (0-6). the name gets copied to the 1st line of the screen's
+ status box.
+ if display is true, the new screen displays. otherwise it's created
+ "in the background".
+ */
+char scr_create(const char *name, char display);
+
+/* destroys a screen (frees up its slot). */
+void scr_destroy(char s);
+
+/* sets the topic for a screen (the 2nd line of the status box).
+ this can't just be another argument to scr_create() because we
+ don't know the topic yet when we create a screen (also, the
+ server and msgs windows don't have topics anyway). */
+void scr_set_topic(char s, const char *topic);
+
+/* switch display to screen s. updates the display list. clears
+ s's active flag. calls scr_show_status(). */
+void scr_display(char s);
+
+/* XXX: does this need to be public? */
+void scr_show_status(char s);
+
+/* calls scr_display() on the currently-visible screen.
+ called by the edit box code, when closing the edit box (user has
+ pressed Enter or aborted the message). */
+void scr_refresh(void);
+
+/* given a channel name, find its screen. if there's no match,
+ returns 0 (the server messages screen) */
+char scr_getbyname(const char *name);
+
+/* print one character to a screen. handles scrolling. will not
+ print an EOL at column 0 (just ignores it). */
+void scr_putc(char s, char c);
+
+/* print text to a screen. handles scrolling. this
+ doesn't have to be the screen being displayed, of course;
+ if it's not, it gets marked as active. */
+void scr_print(char s, const char *text);
+
+/* set a screen's status to active, if it's not the currently-displayed
+ one. scr_print() sets it already, but anything that uses scr_putc()
+ will have to call this. */
+void scr_activate(char s);
+
+/* XXX: this really should be in a utils.c or common.c... */
+void scr_waitvcount(u8 c);
+
+/**** end of public API ****/
+
+/* notes:
+
+If a character gets printed to the bottom right corner of a screen,
+it *doesn't* scroll yet. It will, the next time a character is printed
+to that screen.
+
+This thing uses a modified font that's in ASCII order rather than
+Atari screencode order. So "printing" just means copying... except
+for IRC formatting codes.
+
+scr_print() has to handle formatting: at least it should print inverse
+video for either bold or italic. It will print incoming inverse
+video as-is; it's the caller's responsibility to replace unprintable
+characters (e.g. any char >= 128 might become an inverse question
+mark). This has to happen because nick highlighting will use inverse
+video.
+
+Also, scr_print() should eventually be smart enough to strip out color
+codes.
+
+*/
diff --git a/uitest/test.c b/uitest/test.c
new file mode 100644
index 0000000..93264dd
--- /dev/null
+++ b/uitest/test.c
@@ -0,0 +1,65 @@
+#include <atari.h>
+#include <stdio.h>
+#include <conio.h>
+#include <string.h>
+#include "../src/screen.h"
+#include "../src/edbox.h"
+
+void got_line(void) {
+ scr_print(SCR_CURR, edit_box);
+}
+
+int main() {
+ char i, s;
+ char buf[40];
+
+ OS.color2 = 0xc2;
+ OS.color1 = 0x0e;
+
+ scr_init();
+ edbox_callback = got_line;
+
+ /*
+ for(i = 0; i < 4; i++) {
+ sprintf(buf, "screen%d", i);
+ s = scr_create(buf, 0);
+ scr_set_topic(s + 1, "This is screen #%d.");
+ }
+ */
+
+ for(i = 0; i < 19; i++) {
+ sprintf(buf, "line %d\n", i);
+ scr_print(0, buf);
+ }
+
+ scr_print(0, "Hold Start and press:\n");
+ scr_print(0, "N: Create new screen, C: Close screen\n");
+ scr_print(0, "1-7: Switch to screen (if exists).\n");
+ scr_print(0, "To enter text, just start typing.\n");
+
+ while(1) {
+ if(OS.ch != 0xff) {
+ if(GTIA_READ.consol == 6) { /* start pressed */
+ i = cgetc();
+ if(i >= '1' && i <= '7') {
+ if(scr_active[i - '1'] != SCR_UNUSED)
+ scr_display(i - '1');
+ } else if(i == 'n') {
+ s = scr_create("new screen", 1);
+ if(s != 0xff) {
+ scr_print(0, "Screen #");
+ scr_putc(0, s + '1');
+ scr_print(0, " created.\n");
+ } else {
+ scr_print(scr_current, "Can't create screen (all in use)\n");
+ }
+ } else if(i == 'c') {
+ scr_destroy(scr_current);
+ }
+ } else {
+ edbox_keystroke();
+ }
+ }
+ }
+ return 0;
+}