From 806281f179c9bdf3fc5016baedc2c9d1a999cdf6 Mon Sep 17 00:00:00 2001 From: Maik Fischer Date: Fri, 6 Oct 2017 00:50:00 +0200 Subject: [PATCH] patch charset to include umlauts --- .gitignore | 7 +- Makefile | 18 +++- assets/umlauts.pbm | Bin 0 -> 111 bytes include/vdc_patch_charset.h | 15 +++ include/vdc_util.h | 14 +++ src/general.c | 3 +- src/kasse.c | 27 +++-- src/print_ascii.c | 54 ++++++++++ src/print_charmap.c | 66 +++++++++++++ src/vdc_patch_charset.c | 18 ++++ src/vdc_util.s | 191 ++++++++++++++++++++++++++++++++++++ util/mkfont | 44 +++++++++ 12 files changed, 443 insertions(+), 14 deletions(-) create mode 100644 assets/umlauts.pbm create mode 100644 include/vdc_patch_charset.h create mode 100644 include/vdc_util.h create mode 100644 src/print_ascii.c create mode 100644 src/print_charmap.c create mode 100644 src/vdc_patch_charset.c create mode 100644 src/vdc_util.s create mode 100755 util/mkfont diff --git a/.gitignore b/.gitignore index 7ffcf3d..a45be6e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,16 @@ kasse .cproject .project -*.s -*.o +build/ *.P00 itemz *.d64 include/version.h +include/charset_umlauts.h tags items +credits cat +charmap +ascii *.lbl diff --git a/Makefile b/Makefile index f3d0577..1df105e 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,11 @@ CC=cc65 AS=ca65 LD=cl65 -INCLUDES:=$(wildcard include/*.h) include/version.h +INCLUDES:=$(wildcard include/*.h) include/version.h include/charset_umlauts.h GV:=$(shell git describe --tags --always) CFLAGS= -I include -t c128 -g -.PHONY: include/version.h clean dist-clean format +.PHONY: include/version.h include/charset_umlauts.h clean dist-clean format all: kasse itemz cat @@ -13,11 +13,17 @@ build/%.o: src/%.c ${INCLUDES} ${CC} ${CFLAGS} -O $< -o build/$(addsuffix .s,$(shell basename $< .c)) ${AS} ${CFLAGS} build/$(addsuffix .s,$(shell basename $< .c)) -o $@ +build/%.o: src/%.s + ${AS} ${CFLAGS} $< -o $@ + include/version.h: mkdir -p build echo "#define GV \"${GV}\"" > $@ -kasse: build/config.o build/kasse.o build/general.o build/credit_manager.o build/c128time.o build/print.o +include/charset_umlauts.h: + ./util/mkfont assets/umlauts.pbm chars_umlauts > $@ + +kasse: build/config.o build/kasse.o build/general.o build/credit_manager.o build/c128time.o build/print.o build/vdc_patch_charset.o build/vdc_util.o ${LD} -Ln $@.lbl -t c128 $^ -o $@ itemz: build/config.o build/itemz.o build/general.o build/credit_manager.o build/c128time.o build/print.o @@ -26,6 +32,12 @@ itemz: build/config.o build/itemz.o build/general.o build/credit_manager.o build cat: build/general.o build/cat.o ${LD} -Ln $@.lbl -t c128 $^ -o $@ +charmap: build/print_charmap.o build/vdc_util.o build/vdc_patch_charset.o + ${LD} -Ln $@.lbl -t c128 $^ -o $@ + +ascii: build/print_ascii.o + ${LD} -Ln $@.lbl -t c128 $^ -o $@ + package: all c1541 -format "${GV}",KA d64 kasse.d64 c1541 -attach kasse.d64 -write kasse diff --git a/assets/umlauts.pbm b/assets/umlauts.pbm new file mode 100644 index 0000000000000000000000000000000000000000..c582deb0bbf932cfdd1c010f9697146ce332bf0a GIT binary patch literal 111 zcmWGA;Zjy`4svx2@ei_6aQE~LPzdnzRdCD9DM>9-2um$0&dkqKFw`^TvQRKH;j$@8 r_)(B#!;mInlUA3O#*k){1_VGp1B|v|vrDrB@<9?XaXSz%2Z#~?%U>Us literal 0 HcmV?d00001 diff --git a/include/vdc_patch_charset.h b/include/vdc_patch_charset.h new file mode 100644 index 0000000..ff80c82 --- /dev/null +++ b/include/vdc_patch_charset.h @@ -0,0 +1,15 @@ +#ifndef __VDC_PATCH_CHARSET_H_ +#define __VDC_PATCH_CHARSET_H_ + +void vdc_patch_charset(void); + +#define EURSYM "\xA2" +#define AUML "\x9C" +#define OUML "\x9E" +#define UUML "\x9F" +#define aUML "\xA6" +#define oUML "\xA8" +#define uUML "\xA9" +#define szLIG "\xBF" + +#endif // __VDC_PATCH_CHARSET_H_ diff --git a/include/vdc_util.h b/include/vdc_util.h new file mode 100644 index 0000000..2c10ccd --- /dev/null +++ b/include/vdc_util.h @@ -0,0 +1,14 @@ +#ifndef __VDC_UTIL_H_ +#define __VDC_UTIL_H_ + +extern void __fastcall__ vdc_load_thinfont(void); +extern unsigned char __fastcall__ vdc_read_reg(unsigned char reg); +extern unsigned __fastcall__ vdc_read_addr(unsigned char reg); +extern void __fastcall__ vdc_write_reg(unsigned char reg, unsigned char data); +extern void __fastcall__ vdc_write_addr(unsigned char reg, unsigned addr); +extern void __fastcall__ vdc_read_mem(unsigned dest, unsigned src, unsigned n); +extern void __fastcall__ vdc_write_mem(unsigned dest, const void *src, + unsigned n); +extern void __fastcall__ vdc_load_thinfont(void); + +#endif // __VDC_UTIL_H_ diff --git a/src/general.c b/src/general.c index 3a3750d..ec06ef4 100644 --- a/src/general.c +++ b/src/general.c @@ -10,6 +10,7 @@ #include #include "general.h" +#include "vdc_patch_charset.h" /* * get_input_terminated_by() reads input (handling backspace correctly) until @@ -75,7 +76,7 @@ char retry_or_quit(void) { } char *format_euro(char *s, int maxlen, int cent) { - if (snprintf(s, maxlen, "%3d,%02dEUR", cent / 100, cent % 100) > maxlen) + if (snprintf(s, maxlen, "%3d,%02d" EURSYM, cent / 100, cent % 100) > maxlen) return NULL; return s; } diff --git a/src/kasse.c b/src/kasse.c index 203d70f..4bed5cf 100644 --- a/src/kasse.c +++ b/src/kasse.c @@ -11,6 +11,7 @@ #include #include #include +#include <6502.h> #include "general.h" #include "config.h" @@ -19,6 +20,7 @@ #include "c128time.h" #include "print.h" #include "version.h" +#include "vdc_patch_charset.h" // drucker 4 oder 5 // graphic 4,0,10 @@ -32,7 +34,7 @@ void print_item(BYTE i) { textcolor(TC_YELLOW); cprintf("%2d", i); textcolor(TC_LIGHT_GRAY); - cprintf(": %-" xstr(MAX_ITEM_NAME_LENGTH) "s \xDD%s, %3dx ", + cprintf(": %-" xstr(MAX_ITEM_NAME_LENGTH) "s \xDD%s, %3dx ", status.status[i].item_name, profit, status.status[i].times_sold); } @@ -47,7 +49,7 @@ static void print_screen(void) { exit(1); } textcolor(TC_CYAN); - cprintf("C128-Kassenprogramm (phil_fry, sECuRE, sur5r) " GV "\r\n"); + cprintf("C128-Kassenprogramm (phil_fry, sECuRE, sur5r, mxf) " GV "\r\n"); textcolor(TC_LIGHT_GRAY); cprintf("\r\nUhrzeit: %s (wird nicht aktualisiert)\r\n" "Eingenommen: %s, Verkauft: %ld Dinge, Drucken: %s\r\n", @@ -175,7 +177,7 @@ static signed int buy(char *name, unsigned int price) { continue; } if (c == 27) { - cprintf("Kauf abgebrochen, druecke RETURN...\r\n"); + cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n"); get_input(); return 1; } @@ -192,7 +194,7 @@ static signed int buy(char *name, unsigned int price) { einheiten = atoi(entered) * negative; if (einheiten > 100 || einheiten < -100 || einheiten == 0) { - cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, druecke " + cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, dr" uUML "cke " "RETURN...\r\n"); cgetc(); return 1; @@ -304,7 +306,8 @@ static signed int buy(char *name, unsigned int price) { } textcolor(TC_LIGHT_GREEN); - cprintf("\r\nVerbleibendes Guthaben fuer %s: %s. Druecke RETURN...\r\n", + cprintf("\r\nVerbleibendes Guthaben f" uUML "r %s: %s. Dr" uUML + "cke RETURN...\r\n", nickname, rest); textcolor(TC_LIGHT_GRAY); get_input(); @@ -312,7 +315,7 @@ static signed int buy(char *name, unsigned int price) { } else { textcolor(TC_LIGHT_RED); cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, " - "druecke RETURN...\r\n"); + "dr" uUML "cke RETURN...\r\n"); textcolor(TC_LIGHT_GRAY); get_input(); return 0; @@ -364,7 +367,7 @@ void buy_custom(void) { break; cputc(c); if (c == 27) { - cprintf("Kauf abgebrochen, druecke RETURN...\r\n"); + cprintf("Kauf abgebrochen, dr" uUML "cke RETURN...\r\n"); get_input(); return; } else if (c == '-' && i == 0) @@ -409,6 +412,10 @@ int main(void) { /* clock CPU at double the speed (a whopping 2 Mhz!) */ fast(); + SEI(); + vdc_patch_charset(); + CLI(); + clrscr(); /* Allocate logging buffer memory */ @@ -454,10 +461,14 @@ int main(void) { } else if (*c == 'f') { buy_custom(); } else if (*c == 's') { + cprintf("\r\nsaving items.. "); save_items(); + cprintf("ok\r\nsaving credits.. "); save_credits(); + cprintf("ok\r\nflushing log.. "); log_flush(); - cprintf("\r\nStatefile/Creditfile/Log gesichert, druecke RETURN...\r\n"); + cprintf("ok\r\nStatefile/Creditfile/Log gesichert, dr" uUML + "cke RETURN...\r\n"); get_input(); } else if (*c == 'g') { credit_manager(); diff --git a/src/print_ascii.c b/src/print_ascii.c new file mode 100644 index 0000000..286dc9d --- /dev/null +++ b/src/print_ascii.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include + +//#include "vdc_patch_charset.h" + +int main(void) { + int i = 80; + unsigned char c[3] = {0x20, 0x20, 0}; + unsigned char pos[5]; + + videomode(0x80); + fast(); + clrscr(); + + cputsxy(4, 0, "0 1 2 3 4 5 6 7 8 9 A B C D E F"); + cputs("\r\n"); + + /* unrolled, so compiler can do its magick */ + cputs("20 \x20 \x21 \x22 \x23 \x24 \x25 \x26 \x27 \x28 \x29 \x2a \x2b \x2c " + "\x2d \x2e \x2f \r\n"); + cputs("30 \x30 \x31 \x32 \x33 \x34 \x35 \x36 \x37 \x38 \x39 \x3a \x3b \x3c " + "\x3d \x3e \x3f \r\n"); + cputs("40 \x40 \x41 \x42 \x43 \x44 \x45 \x46 \x47 \x48 \x49 \x4a \x4b \x4c " + "\x4d \x4e \x4f \r\n"); + cputs("50 \x50 \x51 \x52 \x53 \x54 \x55 \x56 \x57 \x58 \x59 \x5a \x5b \x5c " + "\x5d \x5e \x5f \r\n"); + cputs("60 \x60 \x61 \x62 \x63 \x64 \x65 \x66 \x67 \x68 \x69 \x6a \x6b \x6c " + "\x6d \x6e \x6f \r\n"); + cputs("70 \x70 \x71 \x72 \x73 \x74 \x75 \x76 \x77 \x78 \x79 \x7a \x7b \x7c " + "\x7d \x7e \x7f \r\n"); + cputs("80 \x80 \x81 \x82 \x83 \x84 \x85 \x86 \x87 \x88 \x89 \x8a \x8b \x8c " + "\x8d \x8e \x8f \r\n"); + cputs("90 \x90 \x91 \x92 \x93 \x94 \x95 \x96 \x97 \x98 \x99 \x9a \x9b \x9c " + "\x9d \x9e \x9f \r\n"); + cputs("a0 \xa0 \xa1 \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 \xa8 \xa9 \xaa \xab \xac " + "\xad \xae \xaf \r\n"); + cputs("b0 \xb0 \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 \xb8 \xb9 \xba \xbb \xbc " + "\xbd \xbe \xbf \r\n"); + cputs("c0 \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 \xc8 \xc9 \xca \xcb \xcc " + "\xcd \xce \xcf \r\n"); + cputs("d0 \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 \xd8 \xd9 \xda \xdb \xdc " + "\xdd \xde \xdf \r\n"); + cputs("e0 \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 \xe8 \xe9 \xea \xeb \xec " + "\xed \xee \xef \r\n"); + cputs("f0 \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 \xf8 \xf9 \xfa \xfb \xfc " + "\xfd \xfe \xff \r\n"); + + // cputs(EURSYM aUML oUML uUML AUML OUML UUML szLIG "\r\n"); + return 0; +} diff --git a/src/print_charmap.c b/src/print_charmap.c new file mode 100644 index 0000000..be8ca18 --- /dev/null +++ b/src/print_charmap.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include +#include <6502.h> + +#include "vdc_util.h" +#include "vdc_patch_charset.h" + +int main(void) { + int i = 80; + unsigned char c[2] = {0x00, 0x20}; + unsigned char pos[5]; + + /* char attribute, alternate char set, white, full intensity. + * set to 0x0f for normal char set + */ + unsigned char blank = 0x8f; + + videomode(0x80); + fast(); + clrscr(); + + cputsxy(4, 0, "0 1 2 3 4 5 6 7 8 9 A B C D E F"); + + /* Manipulate the VDC with IRQs turned off. + * KERNALs default IRQ handler will also try to read the VDC status + * register, which could interfere with our code trying to read it. + */ + SEI(); + + // vdc_load_thinfont(); + vdc_patch_charset(); + + /* write 16 chars per line */ + do { + if ((*c % 16) == 0) { + sprintf(pos, "%02x ", *c); + vdc_write_mem(i, pos, 4); + i = i + 4; + } + + vdc_write_mem(i, c, 2); + ++(*c); + i = i + 2; + + if ((*c % 16) == 0) { + i = i + 44; + } + } while (*c); + + /* clear attribute mem */ + i = 0; + while (++i <= 2000) + vdc_write_mem(i + 0x800, &blank, 1); + + CLI(); + + /* set cursor, so basic's prompt won't overwrite our output */ + gotoxy(0, 18); + cputs(EURSYM aUML oUML uUML AUML OUML UUML szLIG); + return 0; +} diff --git a/src/vdc_patch_charset.c b/src/vdc_patch_charset.c new file mode 100644 index 0000000..c985aa3 --- /dev/null +++ b/src/vdc_patch_charset.c @@ -0,0 +1,18 @@ +#include + +#include "charset_umlauts.h" +#include "vdc_util.h" + +void vdc_patch_charset(void) { + int i = 0; + /* start of the shifted charset */ + const unsigned int base_addr = 0x3000; + /* EUR Ä Ö Ü ä ö ü ß */ + unsigned char map[8] = {0x62, 0x5C, 0x5E, 0x5F, 0x66, 0x68, 0x69, 0x7F}; + unsigned char *p = map; + + for (; i < sizeof(chars_umlauts); i += 8) { + vdc_write_mem(base_addr + *p * 16, chars_umlauts + i, 8); + ++p; + } +} diff --git a/src/vdc_util.s b/src/vdc_util.s new file mode 100644 index 0000000..7ff3ccf --- /dev/null +++ b/src/vdc_util.s @@ -0,0 +1,191 @@ +;;; -*- tab-width: 8; -*- + .export _vdc_read_reg, _vdc_read_addr, _vdc_read_mem + .export _vdc_write_reg, _vdc_write_addr, _vdc_write_mem + .export _vdc_load_thinfont + .import popa, popax + .importzp ptr1, ptr2 + .include "c128.inc" + .debuginfo on + +;;; useful documentation: +;;; - 6502 instruction/addressing mode overview +;;; - http://www.obelisk.me.uk/6502/index.html +;;; - cc65 assembler interfacing with C +;;; - https://github.com/cc65/wiki/wiki/Parameter-passing-and-calling-conventions +;;; - https://github.com/cc65/wiki/wiki/Parameter-and-return-stacks +;;; - https://github.com/cc65/wiki/wiki/Using-runtime-zeropage-locations-in-assembly-language +;;; - Programming the VDC +;;; - Chapter 10 of http: //www.pagetable.com/docs/Commodore%20128%20Programmer%27s%20Reference%20Guide.pdf + +VDC_ADDR_REG := 19 +VDC_MEM_REG := 31 + +;;; unsigned char __fastcall__ vdc_read_reg (unsigned char reg); +_vdc_read_reg: + ldx #0 ; clear high byte +vdc_read_reg: + sta VDC_INDEX + +@wait: bit VDC_INDEX ; busy wait until vdc is ready + bpl @wait + + lda VDC_DATA + rts + +;;; unsigned __fastcall__ vdc_read_addr (unsigned char reg); +_vdc_read_addr: + tay ; save copy of vdc reg + jsr vdc_read_reg + tax ; save high byte + dey ; set low byte vdc reg + tya + jsr vdc_read_reg + rts + +;;; void __fastcall__ vdc_write_reg (unsigned char reg, unsigned char data); +_vdc_write_reg: + pha + jsr popa + tay + pla +vdc_write_reg: + sty VDC_INDEX + +@wait: bit VDC_INDEX ; busy wait until vdc is ready + bpl @wait + + sta VDC_DATA + rts + +;;; void __fastcall__ vdc_write_addr (unsigned char reg, unsigned addr); +_vdc_write_addr: + pha + jsr popa + tay + pla +vdc_write_addr: + jsr vdc_write_reg + txa ; get high byte of addr + dey + jsr vdc_write_reg + rts + +;;; void __fastcall__ vdc_read_mem (unsigned dest, unsigned src, unsigned n); +_vdc_read_mem: + sta ptr1 ; store n + stx ptr1+1 + + jsr popax + ldy #VDC_ADDR_REG + jsr vdc_write_addr + + jsr popax + sta ptr2 + stx ptr2+1 + + lda #VDC_MEM_REG + sta VDC_INDEX + + ldy #0 ; offset into dest + + ;; first, loop over the high byte of n, 256 times + ldx ptr1+1 ; get high byte of n + beq @low ; skip if zero + +@cpyhi: bit VDC_INDEX + bpl @cpyhi + + lda VDC_DATA + sta (ptr2),y + iny + bne @cpyhi ; have we copied 256 bytes yet? + + inc ptr2+1 ; adjust dest pointer + dex + bne @cpyhi ; read 256 more bytes + +@low: ldx ptr1 ; get low byte of n + beq @done ; skip if zero + +@cpy: bit VDC_INDEX + bpl @cpy + + lda VDC_DATA + sta (ptr2),y + iny + dex + bne @cpy + +@done: rts + +;;; void __fastcall__ vdc_write_mem (unsigned dest, const void* src, unsigned n); +_vdc_write_mem: + sta ptr1 ; store n + stx ptr1+1 + + jsr popax + sta ptr2 + stx ptr2+1 + + jsr popax + ldy #VDC_ADDR_REG + jsr vdc_write_addr + + lda #VDC_MEM_REG + sta VDC_INDEX + + ldy #0 ; offset into dest + + ;; first, loop over the high byte of n + ldx ptr1+1 ; get high byte of n + beq @low ; skip if zero + +@cpyhi: bit VDC_INDEX + bpl @cpyhi + + lda (ptr2),y + sta VDC_DATA + iny + bne @cpyhi ; have we copied 256 bytes yet? + + inc ptr2+1 ; adjust dest pointer + dex + bne @cpyhi ; read 256 more bytes + +@low: ldx ptr1 ; get low byte of n + beq @done ; skip if zero + +@cpy: bit VDC_INDEX + bpl @cpy + + lda (ptr2),y + sta VDC_DATA + iny + dex + bne @cpy + +@done: rts + +;;; void __fastcall__ vdc_load_thinfont (void); +_vdc_load_thinfont: + ;; save MMU register + lda $0 + pha + lda $1 + pha + + ;; map in alternate font + lda #$FF + sta $0 + lda #$33 + sta $1 + + ;; call kernal load font routine + jsr $FF62 + + ;; restore MMU + pla + sta $1 + pla + sta $0 + rts diff --git a/util/mkfont b/util/mkfont new file mode 100755 index 0000000..5af87c4 --- /dev/null +++ b/util/mkfont @@ -0,0 +1,44 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use v5.10; + +my ($fname, $array_name) = @ARGV; + +die "Usage: $0 \n" unless $fname; + +my $fh; +open $fh, '<', $fname or die "$0: $!\n"; + +my $header = <$fh>; +die "$0: $fname: unknown file format\n" unless $header eq "P4\n"; + +my $len; +{ + my $line = <$fh>; + chomp $line; + last if ($len) = $line =~ /^8 (\d+)$/; + redo if $line =~ /^#/; + die "$0: $fname: couldn't parse header\n"; +} + +local $/; +# read rest of file and return a list of the ascii value for each byte +my @bin = map { ord($_) } split //, <$fh>; + +die "$0: $fname should have $len bytes image data, got: " . scalar(@bin) . "\n" + unless scalar(@bin) == $len; + +say "/* autogenerated by util/mkfont */"; +say "const unsigned char $array_name\[$len] = {"; + +# break up in max. 8 bytes per line +while (my @line = splice @bin, 0, 8) { + # seperated by commata, depending on how many bytes we have + my $hexify = join ', ', ('0x%02x') x scalar(@line); + printf " $hexify" , @line; + # if there's more bytes left, seperate next block by a comma + print scalar(@bin) > 0 ? ",\n" : "\n"; +} + +say "};" -- 2.39.5