diff --git a/CMakelists.txt b/CMakelists.txt index 3f48401..1130a01 100644 --- a/CMakelists.txt +++ b/CMakelists.txt @@ -23,12 +23,13 @@ target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE ) target_sources(${CMAKE_PROJECT_NAME} PRIVATE - "src/main.c" - "src/ram.c" + src/main.c + src/ram.c src/cpu.c src/rom.c - src/io.c - src/memoryMap.c + src/memory.c + src/file.c + src/screen.c ) target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE diff --git a/inc/file.h b/inc/file.h new file mode 100644 index 0000000..ebacf7e --- /dev/null +++ b/inc/file.h @@ -0,0 +1,17 @@ +#ifndef FILE_H +#define FILE_H + +#define COMMAND_OPEN 1 +#define COMMAND_CLOSE 2 +#define COMMAND_SELECT_IN 3 +#define COMMAND_SELECT_OUT 4 +#define COMMAND_READ 5 +#define COMMAND_WRITE 6 +#define COMMAND_RESET 7 + +int openFile(int fd, const char *path); +int closeFile(int fd); +int readByte(const int fd, int *data); +int writeByte(const int fd, const int data); + +#endif // FILE_H \ No newline at end of file diff --git a/inc/io.h b/inc/io.h index 2b54519..b3e1bf7 100644 --- a/inc/io.h +++ b/inc/io.h @@ -9,8 +9,9 @@ #define SCREEN_ADDRESS 0 #define KEYBOARD_ADDRESS 1 #define ROM_BANKED_OUT 2 - -int putChar(uint8_t c); -int getChar(void); +#define FILE_COMMAND 3 +#define FILE_DESCRIPTOR 4 +#define FILE_DATA 5 // two bytes, for pointer to string +#define FILE_STATUS 7 #endif // IO_H \ No newline at end of file diff --git a/inc/memoryMap.h b/inc/memory.h similarity index 74% rename from inc/memoryMap.h rename to inc/memory.h index 9834edd..c5ad49c 100644 --- a/inc/memoryMap.h +++ b/inc/memory.h @@ -1,11 +1,13 @@ -#ifndef MEMORY_MAP_H -#define MEMORY_MAP_H +#ifndef MEMORY_H +#define MEMORY_H #include +#include "file.h" #include "io.h" #include "ram.h" #include "rom.h" +#include "screen.h" uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom); void writeMemory(uint16_t address, uint8_t value, uint8_t *ram); @@ -13,4 +15,4 @@ void writeMemory(uint16_t address, uint8_t value, uint8_t *ram); uint8_t readStack(uint8_t address, uint8_t *ram); void writeStack(uint8_t address, uint8_t value, uint8_t *ram); -#endif // MEMORY_MAP_H \ No newline at end of file +#endif // MEMORY_H \ No newline at end of file diff --git a/inc/screen.h b/inc/screen.h new file mode 100644 index 0000000..d6abc90 --- /dev/null +++ b/inc/screen.h @@ -0,0 +1,9 @@ +#ifndef SCREEN_H +#define SCREEN_H + +#include + +int putChar(uint8_t c); +int getChar(void); + +#endif // SCREEN_H \ No newline at end of file diff --git a/rom/defines.s b/rom/defines.s index 21a4610..185825e 100644 --- a/rom/defines.s +++ b/rom/defines.s @@ -1,20 +1,43 @@ ; constants -SPACE_FOR_GOSUB := $3E -STACK_TOP := $FA -WIDTH := 80 ; Default terminal width -WIDTH2 := 70 ; mac column for zoned print -RAMSTART2 := $0400 +SPACE_FOR_GOSUB := $3E +STACK_TOP := $FA +WIDTH := 80 ; Default terminal width +WIDTH2 := 70 ; mac column for zoned print +RAMSTART2 := $0400 -BYTES_FP := 5 -BYTES_PER_ELEMENT := BYTES_FP -BYTES_PER_VARIABLE := BYTES_FP+2 -MANTISSA_BYTES := BYTES_FP-1 -BYTES_PER_FRAME := 2*BYTES_FP+8 -FOR_STACK1 := 2*BYTES_FP+5 -FOR_STACK2 := BYTES_FP+4 +; io registers +IO_SCREEN := $BC00 +IO_KBD := $BC01 +IO_ROM_BANKED_OUT := $BC02 +IO_FILE_COMMAND := $BC03 +IO_FILE_DESCRIPTOR := $BC04 +IO_FILE_DATA := $BC05 +IO_FILE_STATUS := $BC07 -STACK := $0100 -STACK2 := STACK +; io commands +IO_COMMAND_OPEN := 1 +IO_COMMAND_CLOSE := 2 +IO_COMMAND_SELECT_IN := 3 +IO_COMMAND_SELECT_OUT := 4 +IO_COMMAND_READ := 5 +IO_COMMAND_WRITE := 6 +IO_COMMAND_RESET := 7 + +; io status +FILE_STATUS_ERROR := $01 +FILE_STATUS_EOF := $02 +FILE_STATUS_READY := $04 + +BYTES_FP := 5 +BYTES_PER_ELEMENT := BYTES_FP +BYTES_PER_VARIABLE := BYTES_FP+2 +MANTISSA_BYTES := BYTES_FP-1 +BYTES_PER_FRAME := 2*BYTES_FP+8 +FOR_STACK1 := 2*BYTES_FP+5 +FOR_STACK2 := BYTES_FP+4 + +STACK := $0100 +STACK2 := STACK MAX_EXPON = 10 diff --git a/rom/error.s b/rom/error.s index 6c6ef48..305ee15 100644 --- a/rom/error.s +++ b/rom/error.s @@ -14,9 +14,7 @@ define_error ERR_ZERODIV, "DIVISION BY ZERO" define_error ERR_ILLDIR, "ILLEGAL DIRECT" define_error ERR_BADTYPE, "TYPE MISMATCH" define_error ERR_STRLONG, "STRING TOO LONG" -.ifdef CONFIG_FILE define_error ERR_BADDATA, "FILE DATA" -.endif define_error ERR_FRMCPX, "FORMULA TOO COMPLEX" define_error ERR_CANTCONT, "CAN'T CONTINUE" define_error ERR_UNDEFFN, "UNDEF'D FUNCTION" \ No newline at end of file diff --git a/rom/extra.s b/rom/extra.s index 3252367..9f9b9cc 100644 --- a/rom/extra.s +++ b/rom/extra.s @@ -1,34 +1,126 @@ .segment "EXTRA" -LOAD: +; IO + +CHKIN: + stx CURDVC + stx IO_FILE_DESCRIPTOR + lda IO_COMMAND_SELECT_IN + sta IO_FILE_COMMAND rts -SAVE: +CHKOUT: + stx CURDVC + stx IO_FILE_DESCRIPTOR + lda IO_COMMAND_SELECT_OUT + sta IO_FILE_COMMAND rts +CLRCH: + lda #0 + sta CURDVC + lda IO_COMMAND_RESET + sta IO_FILE_COMMAND + rts + +CHRIN: + lda CURDVC + bne @file + lda IO_KBD + bmi @end + jsr CHROUT ; echo +@file: + lda IO_COMMAND_READ + sta IO_FILE_COMMAND +@wait: + lda IO_FILE_STATUS + and #FILE_STATUS_READY + beq @wait + lda IO_FILE_DATA +@end: + rts + +CHROUT: + lda CURDVC + bne @file + sta IO_SCREEN + rts +@file: + sta IO_FILE_DATA + lda IO_COMMAND_WRITE + sta IO_FILE_COMMAND +@wait: + lda IO_FILE_STATUS + and #$4 + beq @wait + rts + +OPEN: + jsr GETBYT + stx IO_FILE_DESCRIPTOR + jsr CHKCOM + jsr FRMEVL + jsr CHKSTR + jsr FREFAC + sta STRNG2 + ldy #0 +@copy: + cpy STRNG2 + beq @done_copy + lda (INDEX),y + sta FILE_NAME_BUFFER,y + iny + bne @copy +@done_copy: + lda #0 + sta FILE_NAME_BUFFER,y + lda #FILE_NAME_BUFFER + sta IO_FILE_DATA+1 + lda #IO_COMMAND_OPEN + sta IO_FILE_COMMAND +@wait: + lda IO_FILE_STATUS + and #FILE_STATUS_READY + beq @wait + lda #FILE_STATUS_READY + and #$01 + bne @error + rts +@error: + jmp IQERR + +CLOSE: + rts + +; Default + ISCNTC: - lda $BC01 + lda IO_KBD cmp #$03 beq :+ rts : jmp STOP -MONRDKEY: - lda $BC01 - bmi :+ - sta $BC00 ; echo -: +SYS: + jsr GTNUM + lda LINNUM + sta SYSJMP+1 + lda LINNUM+1 + sta SYSJMP+2 +SYSJMP: + jsr $FFFF rts -MONCOUT: - sta $BC00 - rts +MONRDKEY := CHRIN +MONCOUT := CHROUT .segment "WOZMON" .include "wozmon.s" .segment "RESETVEC" - .word $0000 ; NMI vector - .word COLD_START ; RESET vector - .word $0000 ; IRQ vector +.word $0000 ; NMI vector +.word COLD_START ; RESET vector +.word $0000 ; IRQ vector diff --git a/rom/init.s b/rom/init.s index 5459b8c..e294e8e 100644 --- a/rom/init.s +++ b/rom/init.s @@ -77,9 +77,9 @@ L4098: sta INPUTBUFFER-4 ldx #TEMPST stx TEMPPT - lda #<$BC00 ; Start of IO + lda #$BC00 + lda #>IO_SCREEN sta MEMSIZ+1 sta FRETOP sty FRETOP+1 diff --git a/rom/inline.s b/rom/inline.s index 26195bd..2bf6443 100644 --- a/rom/inline.s +++ b/rom/inline.s @@ -40,13 +40,9 @@ L2453: jmp L29B9 GETLN: - .ifdef CONFIG_FILE jsr CHRIN ldy CURDVC bne L2465 - .else - jsr MONRDKEY - .endif cmp #$0F bne L2465 pha diff --git a/rom/input.s b/rom/input.s index c0c427b..49372f6 100644 --- a/rom/input.s +++ b/rom/input.s @@ -22,13 +22,11 @@ L2A67: SYNERR4: jmp SYNERR RESPERR: -.ifdef CONFIG_FILE lda CURDVC beq LCA8F ldx #ERR_BADDATA jmp ERROR LCA8F: -.endif lda #ERRREENTRY jsr STROUT @@ -45,7 +43,6 @@ RTS20: GET: jsr ERRDIR ; CBM: if GET#, then switch input -.ifdef CONFIG_FILE cmp #'#' bne LCAB6 jsr CHRGET @@ -55,7 +52,6 @@ GET: jsr CHKIN stx CURDVC LCAB6: -.endif ldx #<(INPUTBUFFER+1) ldy #>(INPUTBUFFER+1) lda #$00 @@ -63,16 +59,13 @@ LCAB6: lda #$40 jsr PROCESS_INPUT_LIST ; CBM: if GET#, then switch input back -.ifdef CONFIG_FILE ldx CURDVC bne LCAD8 -.endif rts ; ---------------------------------------------------------------------------- ; "INPUT#" STATEMENT ; ---------------------------------------------------------------------------- -.ifdef CONFIG_FILE INPUTH: jsr GETBYT lda #$2C @@ -88,7 +81,6 @@ LCAD8: stx CURDVC rts LCAE0: -.endif ; ---------------------------------------------------------------------------- ; "INPUT" STATEMENT @@ -107,30 +99,24 @@ L2A9E: sta INPUTBUFFER-1 LCAF8: jsr NXIN - .ifdef CONFIG_FILE lda CURDVC beq LCB0C - lda Z96 - and #$02 + lda IO_FILE_STATUS + and #FILE_STATUS_EOF beq LCB0C jsr LCAD6 jmp DATA LCB0C: - .endif lda INPUTBUFFER bne L2ABE - .ifdef CONFIG_FILE lda CURDVC bne LCAF8 - .endif clc jmp CONTROL_C_TYPED NXIN: - .ifdef CONFIG_FILE lda CURDVC bne LCB21 - .endif jsr OUTQUES ; '?' jsr OUTSP LCB21: @@ -196,10 +182,8 @@ PROCESS_INPUT_ITEM: bne L2AF8 ; always L2AF0: bmi FINDATA -.ifdef CONFIG_FILE lda CURDVC bne LCB64 -.endif jsr OUTQUES LCB64: jsr NXIN @@ -300,10 +284,8 @@ L2B94: ldy #$00 lda (INPTR),y beq L2BA1 -.ifdef CONFIG_FILE lda CURDVC bne L2BA1 -.endif lda #ERREXTRA jmp STROUT diff --git a/rom/misc1.s b/rom/misc1.s index 5e7aee3..36fb1ce 100644 --- a/rom/misc1.s +++ b/rom/misc1.s @@ -142,7 +142,6 @@ L2963: RET5: rts -.ifdef CONFIG_FILE PRINTH: jsr CMD jmp LCAD6 @@ -157,5 +156,3 @@ LC98F: stx CURDVC plp jmp PRINT -.endif - diff --git a/rom/print.s b/rom/print.s index 7004d68..5f1100f 100644 --- a/rom/print.s +++ b/rom/print.s @@ -43,10 +43,8 @@ L29B9: sta INPUTBUFFER,x ldx #<(INPUTBUFFER-1) ldy #>(INPUTBUFFER-1) - .ifdef CONFIG_FILE lda CURDVC bne L29DD - .endif CRDO: lda #CR @@ -124,7 +122,6 @@ L2A22: jmp L2A22 ; ---------------------------------------------------------------------------- OUTSP: -.ifdef CONFIG_FILE ; on non-screen devices, print SPACE ; instead of CRSR RIGHT lda CURDVC @@ -133,9 +130,6 @@ OUTSP: .byte $2C LCA40: lda #$1D ; CRSR RIGHT -.else - lda #$20 -.endif .byte $2C OUTQUES: lda #$3F diff --git a/rom/program.s b/rom/program.s index fcca936..0df5f34 100644 --- a/rom/program.s +++ b/rom/program.s @@ -20,14 +20,12 @@ MEMERR: ; ---------------------------------------------------------------------------- ERROR: lsr Z14 -.ifdef CONFIG_FILE lda CURDVC ; output beq LC366 ; is screen jsr CLRCH ; otherwise redirect output back to screen lda #$00 sta CURDVC LC366: -.endif jsr CRDO jsr OUTQUES L2329: diff --git a/rom/token.s b/rom/token.s index 59f5b4d..a9c3c4b 100644 --- a/rom/token.s +++ b/rom/token.s @@ -6,9 +6,7 @@ keyword_rts "FOR", FOR keyword_rts "NEXT", NEXT keyword_rts "DATA", DATA -.ifdef CONFIG_FILE keyword_rts "INPUT#", INPUTH -.endif keyword_rts "INPUT", INPUT keyword_rts "DIM", DIM keyword_rts "READ", READ @@ -25,19 +23,15 @@ keyword_rts "WAIT", WAIT keyword_rts "DEF", DEF keyword_rts "POKE", POKE -.ifdef CONFIG_FILE keyword_rts "PRINT#", PRINTH -.endif keyword_rts "PRINT", PRINT, TOKEN_PRINT keyword_rts "CONT", CONT keyword_rts "LIST", LIST keyword_rts "CLEAR", CLEAR -.ifdef CONFIG_FILE keyword_rts "CMD", CMD keyword_rts "SYS", SYS keyword_rts "OPEN", OPEN keyword_rts "CLOSE", CLOSE -.endif keyword_rts "GET", GET keyword_rts "NEW", NEW diff --git a/rom/wozmon.s b/rom/wozmon.s index e607ebb..c9162b2 100644 --- a/rom/wozmon.s +++ b/rom/wozmon.s @@ -33,7 +33,7 @@ BACKSPACE: DEY ; Back up text index. BMI GETLINE ; Beyond start of line, reinitialize. NEXTCHAR: - LDA $BC01 ; Check status. + LDA IO_KBD ; Check status. BMI NEXTCHAR ; Loop until ready. STA IN,Y ; Add to text buffer. JSR ECHO ; Display character. @@ -165,5 +165,5 @@ PRHEX: ADC #$06 ; Add offset for letter. ECHO: - STA $BC00 ; Output character. + STA IO_SCREEN ; Output character. RTS ; Return. \ No newline at end of file diff --git a/rom/zeropage.s b/rom/zeropage.s index 7067380..67c9e9d 100644 --- a/rom/zeropage.s +++ b/rom/zeropage.s @@ -28,7 +28,6 @@ TXPSV: INPUTBUFFER = $0200 .org $12 - CHARAC: .res 1 ENDCHR: @@ -51,7 +50,6 @@ Z14: .res 1 .org $1D - TEMPPT: .res 1 LASTPT: @@ -156,3 +154,6 @@ TXTPTR = <(GENERIC_TXTPTR-GENERIC_CHRGET + CHRGET) CHRGOT = <(GENERIC_CHRGOT-GENERIC_CHRGET + CHRGET) CHRGOT2 = <(GENERIC_CHRGOT2-GENERIC_CHRGET + CHRGET) RNDSEED = <(GENERIC_RNDSEED-GENERIC_CHRGET + CHRGET) +CURDVC: + .res 1 +FILE_NAME_BUFFER = $0300 \ No newline at end of file diff --git a/src/cpu.c b/src/cpu.c index 1dbdae8..5baff85 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -1,6 +1,6 @@ #include "cpu.h" -#include "memoryMap.h" +#include "memory.h" #include "opcodes.h" #define COMBINE(H, L) ((H << 8) | L) diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..4c92210 --- /dev/null +++ b/src/file.c @@ -0,0 +1,63 @@ +#include "file.h" + +#include +#include + +// Up to 256 files can be open +FILE *files[256]; + +int openFile(int fd, const char *path) { + files[fd] = fopen(path, "r+"); + if (files[fd] == NULL) { + return errno; + } + + return 0; +} + +int closeFile(int fd) { + int ret = fclose(files[fd]); + if (ret != 0) { + return errno; + } + + return 0; +} + +int readByte(const int fd, int *data) { + int status = 0; + int count = fread(&data, 1, 1, files[fd]); + // Error or EOF + if (count != 1) { + int error = ferror(files[fd]); + int eof = feof(files[fd]); + if (error != 0) { + status = 0x2; + } else if (eof != 0) { + status = 0x1; + } else { + status = 0x4; + } + } + + return status; +} + +int writeByte(const int fd, const int data) { + int status = 0; + int count = fwrite(&data, 1, 1, files[fd]); + // Error or EOF + if (count != 1) { + int error = ferror(files[fd]); + int eof = feof(files[fd]); + if (error != 0) { + status = 0x2; + } else if (eof != 0) { + status = 0x1; + } else { + status = 0x4; + } + } + + return status; +} \ No newline at end of file diff --git a/src/main.c b/src/main.c index 163f901..157560c 100644 --- a/src/main.c +++ b/src/main.c @@ -46,7 +46,7 @@ int main(void) { printf("6502PC Emulator with MS Basic\n"); printf("CTRL + G to exit\n"); - printf("WOZMON available, BASIC at DF6A\n"); + printf("WOZMON available, BASIC at E032\n"); clock_gettime(CLOCK_MONOTONIC, &emulationStart); diff --git a/src/memoryMap.c b/src/memory.c similarity index 57% rename from src/memoryMap.c rename to src/memory.c index 0e81465..a1583d2 100644 --- a/src/memoryMap.c +++ b/src/memory.c @@ -1,4 +1,4 @@ -#include "memoryMap.h" +#include "memory.h" uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom) { // RAM/ROM @@ -16,10 +16,18 @@ uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom) { case KEYBOARD_ADDRESS: { return getChar(); } + case ROM_BANKED_OUT: { return ram[IO_BASE + ROM_BANKED_OUT]; } + case FILE_DATA: { + return ram[IO_BASE + FILE_DATA]; + } + case FILE_STATUS: { + return ram[IO_BASE + FILE_STATUS]; + } + default: break; } @@ -55,6 +63,33 @@ void writeMemory(uint16_t address, uint8_t value, uint8_t *ram) { ram[IO_BASE + ROM_BANKED_OUT] = value; break; + case FILE_COMMAND: { + switch (ram[IO_BASE + FILE_COMMAND]) { + case COMMAND_OPEN: { + ram[IO_BASE + FILE_STATUS] = 0; + ram[IO_BASE + FILE_STATUS] = openFile(ram[IO_BASE + FILE_DESCRIPTOR], (const char *)&ram[IO_BASE + FILE_DATA]); + break; + } + case COMMAND_CLOSE: { + ram[IO_BASE + FILE_STATUS] = 0; + ram[IO_BASE + FILE_STATUS] = closeFile(ram[IO_BASE + FILE_DESCRIPTOR]); + break; + } + + default: + break; + } + break; + } + case FILE_DESCRIPTOR: { + ram[IO_BASE + FILE_DESCRIPTOR] = value; + break; + } + case FILE_DATA: { + ram[IO_BASE + FILE_DATA] = value; + break; + } + default: break; } diff --git a/src/io.c b/src/screen.c similarity index 90% rename from src/io.c rename to src/screen.c index 3b5460f..b3a5f23 100644 --- a/src/io.c +++ b/src/screen.c @@ -1,38 +1,38 @@ -#include "io.h" - -#include -#include - -int putChar(uint8_t c) { - int _c = putchar(c); - fflush(stdout); - - return _c; -} - -int getChar(void) { - clearerr(stdin); - - int c = getchar(); - - // Specifically send FF for EOF to trigger echo cancellation - if (c == EOF) { - return 0xFF; - } - - // BASIC expects CR always - if (c == '\n') { - return '\r'; - } - - // BASIC is uppercase only - if (c >= 'a' && c <= 'z') { - c = c - 'a' + 'A'; - } - - if (c == 0x07) { - exit(EXIT_SUCCESS); - } - - return c; +#include "screen.h" + +#include +#include + +int putChar(uint8_t c) { + int _c = putchar(c); + fflush(stdout); + + return _c; +} + +int getChar(void) { + clearerr(stdin); + + int c = getchar(); + + // Specifically send FF for EOF to trigger echo cancellation + if (c == EOF) { + return 0xFF; + } + + // BASIC expects CR always + if (c == '\n') { + return '\r'; + } + + // BASIC is uppercase only + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + + if (c == 0x07) { + exit(EXIT_SUCCESS); + } + + return c; } \ No newline at end of file