commit 2d7bfa6436220818afac787dbb92b86433c7c38f Author: Dreaded_X Date: Sat Sep 19 22:16:34 2020 +0200 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e8de739 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.build/ +.clangd +compile_commands.json diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..525d02e --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/bash +rm -rf .build +mkdir .build +# zasm src/crt0.z80 -y -o .build/xed.com -L ../sdcc/device/lib +zasm src/crt0.z80 -y -o .build/xed.com -L lib -I ../sdcc-code/sdcc/device/include diff --git a/include/console.h b/include/console.h new file mode 100644 index 0000000..5b47c96 --- /dev/null +++ b/include/console.h @@ -0,0 +1,20 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +#include + +#define SCREEN_WIDTH 80 +#define SCREEN_HEIGHT 45 + +extern uint8_t console_getchar(); +extern void console_putchar(uint8_t c); +extern void console_printstring(const uint8_t* c); + +extern void console_revon(); +extern void console_revoff(); + +extern void console_clear(); +extern void console_clear_eol(); +extern void console_goto(uint8_t x, uint8_t y); + +#endif diff --git a/lib/___itoa.s b/lib/___itoa.s new file mode 100644 index 0000000..32de73a --- /dev/null +++ b/lib/___itoa.s @@ -0,0 +1,36 @@ +#code _CODE + +#include "___uitoa.s" + +; +;void __itoa(int value, char *string, unsigned char radix); +; +___itoa:: + push ix + ld ix, #0 + add ix, sp +; +; 4(ix) - value +; 6(ix) - string +; 8(ix) - radix +; + ld e, 4 (ix) + ld d, 5 (ix) + bit 7, d + jp Z, ___uitoa_de +;positive/negative numbers are supported only for radix=10 + ld a, 8 (ix) + cp a, #10 + jp NZ, ___uitoa_de +;add minus sign to result and inverse value + ld hl, #0 + or a, a + sbc hl, de + ex de, hl + ld l, 6 (ix) + ld h, 7 (ix) + ld (hl), #0x2D ;minus symbol + inc hl + ld 6 (ix), l + ld 7 (ix), h + jp ___uitoa_dehl diff --git a/lib/___sdcc_call_hl.s b/lib/___sdcc_call_hl.s new file mode 100644 index 0000000..9086c7b --- /dev/null +++ b/lib/___sdcc_call_hl.s @@ -0,0 +1,37 @@ +;-------------------------------------------------------------------------- +; crtcall.s +; +; Copyright (C) 2011, Maarten Brock +; +; This library is free software; you can redistribute it and/or modify it +; under the terms of the GNU General Public License as published by the +; Free Software Foundation; either version 2, or (at your option) any +; later version. +; +; This library is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this library; see the file COPYING. If not, write to the +; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, +; MA 02110-1301, USA. +; +; As a special exception, if you link this library with other files, +; some of which are compiled with SDCC, to produce an executable, +; this library does not by itself cause the resulting executable to +; be covered by the GNU General Public License. This exception does +; not however invalidate any other reasons why the executable file +; might be covered by the GNU General Public License. +;-------------------------------------------------------------------------- + + .area _CODE + + .globl ___sdcc_call_hl + +; The Z80 has the jp (hl) instruction, which is perfect for implementing function pointers. + +___sdcc_call_hl: + jp (hl) + diff --git a/lib/___strreverse.s b/lib/___strreverse.s new file mode 100644 index 0000000..baeb608 --- /dev/null +++ b/lib/___strreverse.s @@ -0,0 +1,30 @@ +#code _CODE +; +;void __reverse(char *beg, char *end); +; +___strreverse:: + pop bc + pop de + pop hl + push hl + push de + push bc +; +;in: HL - pointer to end of string (null symbol), DE - pointer to start of string +; +___strreverse_reg:: + jr 110$ +100$: + add hl, de + ld a, (de) + ld c, (hl) + ld (hl), a + ld a, c + ld (de), a + inc de +110$: + dec hl + or a, a + sbc hl, de + jr NC, 100$ + ret diff --git a/lib/___uitoa.s b/lib/___uitoa.s new file mode 100644 index 0000000..a9c1bef --- /dev/null +++ b/lib/___uitoa.s @@ -0,0 +1,172 @@ +#code _CODE + +#include "___uitobcd.s" +#include "___strreverse.s" + +; +;void __uitoa(unsigned int value, char *string, unsigned char radix); +; +___uitoa:: + push ix + ld ix, #0 + add ix, sp +; +; 4(ix) - value +; 6(ix) - string +; 8(ix) - radix +; + ld e, 4 (ix) + ld d, 5 (ix) +; +___uitoa_de: + ld l, 6 (ix) + ld h, 7 (ix) +; +___uitoa_dehl: + ld a, e + or a, d + jr NZ, 100$ +; + ld (hl), #0x30 + inc hl + jp 190$ +100$: + ld a, 8 (ix) + cp a, #10 ;most popular radix + jr NZ, 110$ +; +;-------- decimal convertion +; this algorithm up to 2 times faster than generic +; + ld c, l + ld b, h + ld hl, #-4 + add hl, sp + ld sp, hl + push bc + push hl + push de + call ___uitobcd + ld hl, #4 + add hl, sp + ld sp, hl + pop de ;DE - pointer to string + inc hl + inc hl ;HL - pointer to BCD value + ld b, #3 ;number of bytes in BCD value + ld a, #0x30 ;ASCII code of '0' +103$: + rrd + ld (de), a + inc de + rrd + ld (de), a + inc de + inc hl + djnz 103$ +; +; pop af +; pop af +;skip trailing zeroes + ld b, #5 ;real BCD number is at most 5 digits + dec de ;so always skip last zero +105$: + dec de + ld a, (de) + cp a, #0x30 + jr NZ, 107$ ;break loop if non-zero found + djnz 105$ +107$: + inc de ;always point to symbol next to last significant + ex de, hl + jr 190$ +; +;--------------------------- +; +110$: + cp a, #2 + jr C, 190$ ;radix is less than 2 +; + ld c, a + dec c + and a, c + jr NZ, 150$ +; +;-------- radix is power of 2 +; +; DE - value, HL - pointer to string, C - mask +120$: + ld a, e + ld b, c +125$: + srl d + rr e + srl b + jr NZ, 125$ +; + and a, c + add a, #0x30 + cp a, #0x3A ;convert to 0...9A...Z + jr C, 130$ + add a, #7 +130$: + ld (hl), a + inc hl + ld a, e + or a, d + jr NZ, 120$ + jr 190$ +; +;--------------------------- +; +;-------- custom radix (generic algorithm) +; +150$: + ex de, hl +160$: + ld c, 8 (ix) + call ___divu16_8 + add a, #0x30 + cp a, #0x3A + jr C, 165$ + add a, #7 +165$: + ld (de), a + inc de + ld a, l + or h + jr NZ, 160$ + ex de, hl +; jr 190$ +; +;--------------------------- +; +;-------- finish string and reverse order +190$: + ld (hl), #0 + ld e, 6 (ix) + ld d, 7 (ix) + call ___strreverse_reg + ld sp, ix + pop ix + ret +; +; +;in: HL - divident, C - divisor +;out: HL - quotient, A - remainder +___divu16_8: + xor a, a + ld b, #16 +100$: + add hl, hl + rla + jr c, 110$ + cp a, c + jr c, 120$ +110$: + sub a, c + inc l +120$: + djnz 100$ + ret + diff --git a/lib/___uitobcd.s b/lib/___uitobcd.s new file mode 100644 index 0000000..cc7cd91 --- /dev/null +++ b/lib/___uitobcd.s @@ -0,0 +1,57 @@ +#code _CODE + +; void __uitobcd (unsigned int v, unsigned char bcd[3]) +; __uitobcd converts v to BCD representation to the bcd. +; bcd[] will contain BCD value. +; +___uitobcd: + push ix + ld ix, #0 + add ix, sp +; + ld bc, #0x1000 + ld d, c + ld e, c + ld l, 4 (ix) + ld h, 5 (ix) +; +;--- begin speed optimization +; + ld a, h + or a, a + jr NZ, 100$ +; + ld h, l + srl b +; +;--- end speed optimization +; +; HL - binary value +; CDE - future BCD value +; B - bits count (16) +100$: + add hl, hl + ld a, e + adc a, a + daa + ld e, a + ld a, d + adc a, a + daa + ld d, a + ld a, c + adc a, a + daa + ld c, a + djnz 100$ +; + ld l, 6 (ix) + ld h, 7 (ix) + ld (hl), e + inc hl + ld (hl), d + inc hl + ld (hl), c +; + pop ix + ret diff --git a/src/console.c b/src/console.c new file mode 100644 index 0000000..be438a1 --- /dev/null +++ b/src/console.c @@ -0,0 +1,40 @@ +#include +#include "console.h" + +void console_printstring(const uint8_t* c) { + while (*c != 0x00) { + console_putchar(*c); + c++; + } +} + +void console_revon() { + console_printstring("\033[7m"); +} + +void console_revoff() { + console_printstring("\033[0m"); +} + +void console_clear() { + console_printstring("\033[2J"); +} + +void console_clear_eol() { + console_printstring("\033[K"); +} + +void console_goto(uint8_t x, uint8_t y) { + if (!x && !y) { + console_printstring("\033[H"); + } else { + console_printstring("\033["); + static char buffer[10]; + __itoa(y, buffer, 10); + console_printstring(buffer); + console_putchar(';'); + __itoa(x, buffer, 10); + console_printstring(buffer); + console_putchar('H'); + } +} diff --git a/src/console.z80 b/src/console.z80 new file mode 100644 index 0000000..191e01c --- /dev/null +++ b/src/console.z80 @@ -0,0 +1,24 @@ +#code _CODE + +bios_call: + ld hl, (1) + add l + ld l, a + jp nc, bios_call_cont + inc h +bios_call_cont: + jp (hl) + +_console_getchar:: + ld a, 6 + call bios_call + ld l, a + ret + +_console_putchar:: + ld iy, 2 + add iy, sp + ld c, (iy) + ld a, 9 + call bios_call + ret diff --git a/src/crt0.z80 b/src/crt0.z80 new file mode 100644 index 0000000..0e38b5d --- /dev/null +++ b/src/crt0.z80 @@ -0,0 +1,83 @@ +#target bin + +#code _HOME, 0x100 +#code _CODE +#code _GSINIT +#code _GSFINAL +#code _INITIALIZER +#code _CABS +#code _CODE_END + +#data _DATA, _CODE_END +#data _INITIALIZED +#data _DABS +#data _HEAP + +#cflags -I../include +#include "xed.c" +#include "console.c" +#include "console.z80" +#include standard library + +#code _HOME +init: + ld a, 0x77 + out (0x80), a + ; Store the old stack location before setting new + ld (oldstack), sp + ld sp, #stack_end + + ; Initialise global variables + call gsinit + + ; Execute program + call _main + + ; Run the exit code + jp _exit + + +#code _CODE +; Get char through bdos +_putchar:: + ld iy, 2 + add iy, sp + ld e, (iy) + ld c, 2 + call 5 + ld l, e + ret + +; Put char through bdos +_getchar:: + ld c, 1 + call 5 + ret + +; Set the old stack back and return +; This needs to be adjusted if the program overwrites the ccp +_exit:: + ld sp, (oldstack) + ret + +#code _GSINIT +; Initialze data +gsinit:: + ld bc, _INITIALIZER_size + ld a, b + or a, c + jr z, gsinit_next + ld de, _INITIALIZED + ld hl, _INITIALIZER + ldir + +gsinit_next: +#code _GSFINAL + ret + +#data _DATA +oldstack: + .ds 2 +stack: + .ds 128 +stack_end: diff --git a/src/xed.c b/src/xed.c new file mode 100644 index 0000000..c94eb42 --- /dev/null +++ b/src/xed.c @@ -0,0 +1,208 @@ +#include +#include +#include "console.h" + +// NOTES: +// This is going to be a line based editor +// Lines are gonig to be max 80 char so we do nog have to deal with text wrap around +// +// A line is going to be split around the cursor +// If the cursor moves we need to move chars from one side to the other +// +// As an optimization we only have to redraw the section after the cursor +// As this is this is the only text that needs to move as a result of insertions + +void exit(); + +uint8_t line_buffer[SCREEN_WIDTH+2] = {0}; +uint8_t* gap_start = line_buffer; +uint8_t* gap_end = line_buffer + SCREEN_WIDTH; + +uint8_t line = 0; +uint8_t column = 0; + +uint8_t next() { + if (column < 80) { + uint8_t* p = gap_end+1; + uint8_t temp = *gap_start; + + *gap_start = *p; + *p = temp; + gap_start++; + gap_end++; + + column++; + + return 1; + } + return 0; +} + +// @todo Add boundary checking +uint8_t previous() { + if (column > 0) { + uint8_t* p = gap_start-1; + uint8_t temp = *gap_end; + + *gap_end = *p; + *p = temp; + gap_start--; + gap_end--; + + column--; + + return 1; + } + return 0; +} + +uint8_t insert(uint8_t c) { + if (column < 80) { + *gap_start = c; + gap_start++; + column++; + return 1; + } + return 0; +} + +uint8_t remove() { + if (column > 0) { + gap_start--; + *gap_start = 0; + column--; + + return 1; + } + return 0; +} + +typedef void (*mode_function)(uint8_t); +struct Mode { + const char* name; + uint8_t id; + mode_function function; +}; + +struct Mode* current_mode; + +void normal_function(uint8_t c); +void insert_function(uint8_t c); + +struct Mode normal_mode = {"NORMAL", 0, normal_function}; +struct Mode insert_mode = {"INSERT", 1, insert_function}; + +uint8_t last_mode_id = 0xFF; + +void insert_function(uint8_t c) { + // We can optimize this by only drawing the gap end part + console_goto(column+1, line+1); + console_clear_eol(); + /* console_printstring(line_buffer); */ + console_printstring(gap_end+1); + + console_goto(column+1, line+1); + + switch (c) { + // Backspace removes last char + case 8: + // @todo Figure out backspace + remove(); + break; + + // Escape moves back to normal mode + case 27: + current_mode = &normal_mode; + return; + + default: + if (insert(c)) { + console_putchar(c); + } + break; + } +} + +void normal_function(uint8_t c) { + switch (c) { + case 'l': + next(); + break; + + case 'h': + previous(); + break; + + case 'i': + current_mode = &insert_mode; + break; + + case 'a': + next(); + current_mode = &insert_mode; + break; + + case 27: + console_clear(); + console_goto(0, 0); + + exit(); + } +} + +int main() { + console_clear(); + console_goto(0, 0); + + static char buffer[10]; + for (int b = 0; b < 16; ++b) { + console_printstring("\033["); + if (b < 8) { + __itoa(b+40, buffer, 10); + } else { + __itoa(b+100-8, buffer, 10); + } + console_printstring(buffer); + console_putchar('m'); + + for (int f = 0; f < 16; ++f) { + + console_printstring("\033["); + if (f < 8) { + __itoa(f+30, buffer, 10); + } else { + __itoa(f+90-8, buffer, 10); + } + console_printstring(buffer); + console_putchar('m'); + + console_printstring("Test"); + } + + console_printstring("\n\r"); + } + + console_printstring("\033[0m"); + + return 0; + + console_clear(); + current_mode = &normal_mode; + + while (1) { + if (last_mode_id != current_mode->id) { + console_goto(0, SCREEN_HEIGHT); + console_clear_eol(); + console_revon(); + console_printstring(current_mode->name); + console_revoff(); + last_mode_id = current_mode->id; + } + + console_goto(column+1, line+1); + + char c = console_getchar(); + + current_mode->function(c); + } +} diff --git a/upload.sh b/upload.sh new file mode 100755 index 0000000..d44024b --- /dev/null +++ b/upload.sh @@ -0,0 +1,2 @@ +#!/bin/bash +cd ../z80-cpm && ./build.py && sudo dd if=.build/disk.img of=/dev/sdb