Skip to content

Commit

Permalink
pound: start my own editor
Browse files Browse the repository at this point in the history
  • Loading branch information
ivop committed Sep 9, 2024
1 parent 215aa53 commit 0c56d4e
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 0 deletions.
2 changes: 2 additions & 0 deletions apps/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def asm(self, name, src: Target = None, deps: Targets = []):
for prog in ["asm", "attr", "copy", "stat", "submit", "objdump", "qe", "life", "ansiterm"]:
llvmprogram(name=prog, srcs=["./%s.c" % prog], deps=["lib+cpm65"])

llvmprogram(name="pound", srcs=["./pound.c"], deps=["lib+cpm65", "lib+zmalloc"])

# Source code.

for prog in ["cls", "bedit", "dump", "ls"]:
Expand Down
161 changes: 161 additions & 0 deletions apps/pound.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* pound - half a kilo editor
*
* Copyright © 2024 by Ivo van Poorten
*
* Based on Build Your Own Text Editor
* (https://viewsourcecode.org/snaptoken/kilo/)
*/

//#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <cpm.h>
#include "lib/zmalloc.h"
#include "lib/screen.h"

struct erow {
int size;
char *chars;
};

struct editorConfig {
uint8_t screenrows, screencols;
unsigned int cx, cy;
unsigned int numrows;
struct erow *row;
} E;

#define CTRL(x) ((x)&0x1f)

void die(char *reason, bool clear) {
if (clear) screen_clear();
cpm_printstring(reason);
cpm_printstring("\r\n");
cpm_warmboot();
}

// -------------------- ROW OPERATIONS --------------------

// XXX check return values of zrealloc and zmalloc
void editorAppendRow(char *s, size_t len) {
E.row = zrealloc(E.row, sizeof(struct erow) * (E.numrows + 1));

int at = E.numrows;
E.row[at].size = len;
E.row[at].chars = zmalloc(len + 1);
memcpy(E.row[at].chars, s, len);
E.row[at].chars[len] = '\0';
E.numrows++;
}

// -------------------- FILE INPUT --------------------

void editorOpen(void) {
cpm_fcb.cr = 0;
if (cpm_open_file(&cpm_fcb)) die("Unable to read file", false);

cpm_set_dma(cpm_default_dma);

char *line = "Hello, world!";
size_t linelen = 13;

editorAppendRow(line, linelen);
editorAppendRow(line, linelen);
editorAppendRow(line, linelen);

cpm_close_file(&cpm_fcb);
}

// -------------------- SCREEN OUTPUT --------------------

void editorRefreshScreen(void) {
screen_showcursor(0);
for (int y=0; y<E.screenrows; y++) {
if (y >= E.numrows) {
screen_setcursor(0,y);
screen_putchar('~');
} else {
screen_setcursor(0,y);
int len = E.row[y].size;
if (len > E.screencols) len = E.screencols;
for (int x=0; x<len; x++)
screen_putchar(E.row[y].chars[x]);
}
screen_clear_to_eol();
}
screen_setcursor(E.cx, E.cy);
screen_showcursor(1);
}

// -------------------- KEYBOARD INPUT --------------------

void editorProcessKeypress(void) {
static bool ctrlk = false;
char c = screen_waitchar();

if (ctrlk) {
switch(c) {
case 'q':
case 'Q':
case CTRL('Q'):
die("Done.", true);
break;
}
ctrlk = false;
return;
}

switch(c) {
case CTRL('E'): // up
if (E.cy) E.cy--;
break;
case CTRL('X'): // down
if (E.cy != E.screenrows-1) E.cy++;
break;
case CTRL('S'): // left
if (E.cx) E.cx--;
break;
case CTRL('D'): // right
if (E.cx != E.screencols-1) E.cx++;
break;
case CTRL('R'): // page up
break;
case CTRL('C'): // page down
break;
case CTRL('K'):
ctrlk = true;
break;
}
}

// -------------------- INIT --------------------

void initEditor(void) {
if (!screen_init()) die("No SCREEN", false);

uint16_t tpa = cpm_bios_gettpa();
uint8_t *top = (uint8_t *) (tpa & 0xff00);
zmalloc_init(cpm_ram, 4096); // test low RAM
// zmalloc_init(cpm_ram, top - cpm_ram);

screen_getsize(&E.screencols, &E.screenrows);
E.screencols++;
E.screenrows++;
E.cx = E.cy = 0;
E.numrows = 0;
E.row = NULL;
}

// -------------------- MAIN --------------------

void main(void) {
initEditor();
editorOpen();

while(1) {
editorRefreshScreen();
editorProcessKeypress();
}
}
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"0:objdump.com": "apps+objdump",
"0:kbdtest.com": "apps+kbdtest",
"0:ansiterm.com": "apps+ansiterm",
"0:pound.com": "apps+pound",
}

BIG_APPS_SRCS = {}
Expand Down
7 changes: 7 additions & 0 deletions lib/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,10 @@
"lib/serial.h": "./serial.h"},
deps=["include"],
)

llvmclibrary(
name="zmalloc",
srcs=["./zmalloc.c"],
hdrs={"lib/zmalloc.h": "./zmalloc.h"},
deps=["lib/zmalloc.h"],
)
134 changes: 134 additions & 0 deletions lib/zmalloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Simple first-fit memory allocator for a fixed size memory pool
*
* Copyright © 2024 by Ivo van Poorten
*
* Use case: small memory systems without an OS, retro computing
*
* zmalloc: first-fit, fit at end of free block (faster mallocs, mostly)
* zfree: free and merge with previous and/or next free blocks
* zrealloc: smaller than current size - block_info_size - 1, shrink and
* create new free block, possible merged with next free
* bigger, alloc new, copy, free old
*/

#include <stdbool.h>
#include <string.h> // memset, memcpy
#include <stdlib.h> // abort

static void *base;

struct block_info;

struct block_info {
bool free;
size_t size;
struct block_info *prev;
struct block_info *next;
};

#define block_info_size (sizeof(struct block_info))

void zmalloc_init(void *start, size_t size) {
base = start;
struct block_info *p = base;
p->free = 1;
p->size = size - block_info_size;
p->prev = p->next = NULL;
}

void *zmalloc(size_t size) {
if (!size) return NULL;

struct block_info *p;

for (p = base; p && !(p->free && p->size >= size); p = p->next) ; // find
if (!p) return NULL;

if (p->size - size > block_info_size) { // split, alloc at end
struct block_info *q = (void *) p + p->size - size;
p->size -= size + block_info_size;
q->size = size;
q->next = p->next;
p->next = q;
q->prev = p;
if (q->next) q->next->prev = q;
p = q;
}

p->free = 0;
return (void *) p + block_info_size;
}

void *zcalloc(size_t nmemb, size_t size) {
size_t nsize = nmemb * size;
void *p = zmalloc(nsize);
if (p) memset(p, 0, nsize);
return p;
}

static void merge_with_next_free(struct block_info *p) {
if (p->next && p->next->free) {
struct block_info *q = p->next;
p->size += q->size + block_info_size;
p->next = q->next;
if (p->next) p->next->prev = p;
}
}

void zfree(void *ptr) {
if (!ptr) abort();

struct block_info *p = ptr - block_info_size;
p->free = 1;

if (p->prev && p->prev->free) { // if free, merge with previous block
struct block_info *q = p->prev;
q->size += p->size + block_info_size;
q->next = p->next;
if (q->next) q->next->prev = q;
p = q;
}
merge_with_next_free(p);
}

void *zrealloc(void *ptr, size_t size) {
if (!ptr) return zmalloc(size);
if (!size) { zfree(ptr); return NULL; }

struct block_info *p = (void *) ptr - block_info_size;

if (size > p->size) { // bigger
// allocate new, copy, and free old
void *q = zmalloc(size);
if (!q) return NULL;
memcpy(q, ptr, p->size);
zfree(ptr);
return q;
} else if (p->size > size + block_info_size + 1) { // smaller enough
// split and create new free block
struct block_info *q = p + block_info_size + size;
q->next = p->next;
p->next = q;
q->prev = p;
if (q->next) q->next->prev = q;
q->size = p->size - block_info_size - size;
p->size = size;
q->free = 1;
merge_with_next_free(q);
}
return ptr;
}

#ifdef ZMALLOC_DEBUG
#include <stdio.h>
void print_memory(void) {
struct block_info *p;
int c;

for (c = 0, p = base ; p ; p = p->next, c++) {
printf("block %d, %s, size: %ld\n", c, p->free ? "free" : "used", p->size);
}
putchar('\n');
}
#endif
12 changes: 12 additions & 0 deletions lib/zmalloc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once
#include <stddef.h>

void zmalloc_init(void *start, size_t size);
void *zmalloc(size_t size);
void *zcalloc(size_t nmemb, size_t size);
void zfree(void *ptr);
void *zrealloc(void *ptr, size_t size);

#ifdef ZMALLOC_DEBUG
void print_memory(void);
#endif

0 comments on commit 0c56d4e

Please sign in to comment.