From 1b19c840084fec3e77f1ee315688317e01e2b6b5 Mon Sep 17 00:00:00 2001 From: morphinejh <78104266+morphinejh@users.noreply.github.com> Date: Fri, 5 Dec 2025 14:30:29 -0500 Subject: [PATCH 1/7] R49 updates and move to CMake --- CMakeLists.txt | 3 + src/cpu/mnemonics.h | 579 --------- src/cpu/tables.h | 121 -- src/extern/serialuart/serialib/serialib.cpp | 1149 +++++++++++++++++ src/extern/serialuart/serialib/serialib.h | 273 ++++ src/extern/serialuart/serialuartTL16C2550.cpp | 551 ++++++++ src/extern/serialuart/serialuartTL16C2550.hpp | 64 + src/extern/serialuart/serialuart_wrapper.cpp | 64 + src/extern/serialuart/serialuart_wrapper.h | 42 + src/main.c | 97 ++ src/memory.c | 33 +- 11 files changed, 2274 insertions(+), 702 deletions(-) delete mode 100644 src/cpu/mnemonics.h delete mode 100644 src/cpu/tables.h create mode 100644 src/extern/serialuart/serialib/serialib.cpp create mode 100644 src/extern/serialuart/serialib/serialib.h create mode 100644 src/extern/serialuart/serialuartTL16C2550.cpp create mode 100644 src/extern/serialuart/serialuartTL16C2550.hpp create mode 100644 src/extern/serialuart/serialuart_wrapper.cpp create mode 100644 src/extern/serialuart/serialuart_wrapper.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f0b41969..3bd8262c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,6 +202,9 @@ set(X16_SOURCES src/ymglue.cpp src/midi.c src/extern/ymfm/src/ymfm_opm.cpp + src/extern/serialuart/serialib/serialib.cpp + src/extern/serialuart/serialuart_wrapper.cpp + src/extern/serialuart/serialuartTL16C2550.cpp ) if(WIN32) diff --git a/src/cpu/mnemonics.h b/src/cpu/mnemonics.h deleted file mode 100644 index 2e6ccda5..00000000 --- a/src/cpu/mnemonics.h +++ /dev/null @@ -1,579 +0,0 @@ -/* Generated by buildtables.py */ - -static const char *mnemonics_c02[256] = { - // $0X - /* $00 */ "brk #$%02x", - /* $01 */ "ora ($%02x,x)", - /* $02 */ "nop #$%02x", - /* $03 */ "nop ", - /* $04 */ "tsb $%02x", - /* $05 */ "ora $%02x", - /* $06 */ "asl $%02x", - /* $07 */ "rmb0 $%02x", - /* $08 */ "php ", - /* $09 */ "ora #$%%0%hhux", - /* $0A */ "asl a", - /* $0B */ "nop ", - /* $0C */ "tsb $%04x", - /* $0D */ "ora $%04x", - /* $0E */ "asl $%04x", - /* $0F */ "bbr0 $%02x, $%04x", - - // $1X - /* $10 */ "bpl $%02x", - /* $11 */ "ora ($%02x),y", - /* $12 */ "ora ($%02x)", - /* $13 */ "nop ", - /* $14 */ "trb $%02x", - /* $15 */ "ora $%02x,x", - /* $16 */ "asl $%02x,x", - /* $17 */ "rmb1 $%02x", - /* $18 */ "clc ", - /* $19 */ "ora $%04x,y", - /* $1A */ "inc a", - /* $1B */ "nop ", - /* $1C */ "trb $%04x", - /* $1D */ "ora $%04x,x", - /* $1E */ "asl $%04x,x", - /* $1F */ "bbr1 $%02x, $%04x", - - // $2X - /* $20 */ "jsr $%04x", - /* $21 */ "and ($%02x,x)", - /* $22 */ "nop #$%02x", - /* $23 */ "nop ", - /* $24 */ "bit $%02x", - /* $25 */ "and $%02x", - /* $26 */ "rol $%02x", - /* $27 */ "rmb2 $%02x", - /* $28 */ "plp ", - /* $29 */ "and #$%%0%hhux", - /* $2A */ "rol a", - /* $2B */ "nop ", - /* $2C */ "bit $%04x", - /* $2D */ "and $%04x", - /* $2E */ "rol $%04x", - /* $2F */ "bbr2 $%02x, $%04x", - - // $3X - /* $30 */ "bmi $%02x", - /* $31 */ "and ($%02x),y", - /* $32 */ "and ($%02x)", - /* $33 */ "nop ", - /* $34 */ "bit $%02x,x", - /* $35 */ "and $%02x,x", - /* $36 */ "rol $%02x,x", - /* $37 */ "rmb3 $%02x", - /* $38 */ "sec ", - /* $39 */ "and $%04x,y", - /* $3A */ "dec a", - /* $3B */ "nop ", - /* $3C */ "bit $%04x,x", - /* $3D */ "and $%04x,x", - /* $3E */ "rol $%04x,x", - /* $3F */ "bbr3 $%02x, $%04x", - - // $4X - /* $40 */ "rti ", - /* $41 */ "eor ($%02x,x)", - /* $42 */ "nop #$%02x", - /* $43 */ "nop ", - /* $44 */ "nop #$%02x", - /* $45 */ "eor $%02x", - /* $46 */ "lsr $%02x", - /* $47 */ "rmb4 $%02x", - /* $48 */ "pha ", - /* $49 */ "eor #$%%0%hhux", - /* $4A */ "lsr a", - /* $4B */ "nop ", - /* $4C */ "jmp $%04x", - /* $4D */ "eor $%04x", - /* $4E */ "lsr $%04x", - /* $4F */ "bbr4 $%02x, $%04x", - - // $5X - /* $50 */ "bvc $%02x", - /* $51 */ "eor ($%02x),y", - /* $52 */ "eor ($%02x)", - /* $53 */ "nop ", - /* $54 */ "nop #$%02x", - /* $55 */ "eor $%02x,x", - /* $56 */ "lsr $%02x,x", - /* $57 */ "rmb5 $%02x", - /* $58 */ "cli ", - /* $59 */ "eor $%04x,y", - /* $5A */ "phy ", - /* $5B */ "nop ", - /* $5C */ "nop ", - /* $5D */ "eor $%04x,x", - /* $5E */ "lsr $%04x,x", - /* $5F */ "bbr5 $%02x, $%04x", - - // $6X - /* $60 */ "rts ", - /* $61 */ "adc ($%02x,x)", - /* $62 */ "nop #$%02x", - /* $63 */ "nop ", - /* $64 */ "stz $%02x", - /* $65 */ "adc $%02x", - /* $66 */ "ror $%02x", - /* $67 */ "rmb6 $%02x", - /* $68 */ "pla ", - /* $69 */ "adc #$%%0%hhux", - /* $6A */ "ror a", - /* $6B */ "nop ", - /* $6C */ "jmp ($%04x)", - /* $6D */ "adc $%04x", - /* $6E */ "ror $%04x", - /* $6F */ "bbr6 $%02x, $%04x", - - // $7X - /* $70 */ "bvs $%02x", - /* $71 */ "adc ($%02x),y", - /* $72 */ "adc ($%02x)", - /* $73 */ "nop ", - /* $74 */ "stz $%02x,x", - /* $75 */ "adc $%02x,x", - /* $76 */ "ror $%02x,x", - /* $77 */ "rmb7 $%02x", - /* $78 */ "sei ", - /* $79 */ "adc $%04x,y", - /* $7A */ "ply ", - /* $7B */ "nop ", - /* $7C */ "jmp ($%04x,x)", - /* $7D */ "adc $%04x,x", - /* $7E */ "ror $%04x,x", - /* $7F */ "bbr7 $%02x, $%04x", - - // $8X - /* $80 */ "bra $%02x", - /* $81 */ "sta ($%02x,x)", - /* $82 */ "nop #$%02x", - /* $83 */ "nop ", - /* $84 */ "sty $%02x", - /* $85 */ "sta $%02x", - /* $86 */ "stx $%02x", - /* $87 */ "smb0 $%02x", - /* $88 */ "dey ", - /* $89 */ "bit #$%%0%hhux", - /* $8A */ "txa ", - /* $8B */ "nop ", - /* $8C */ "sty $%04x", - /* $8D */ "sta $%04x", - /* $8E */ "stx $%04x", - /* $8F */ "bbs0 $%02x, $%04x", - - // $9X - /* $90 */ "bcc $%02x", - /* $91 */ "sta ($%02x),y", - /* $92 */ "sta ($%02x)", - /* $93 */ "nop ", - /* $94 */ "sty $%02x,x", - /* $95 */ "sta $%02x,x", - /* $96 */ "stx $%02x,y", - /* $97 */ "smb1 $%02x", - /* $98 */ "tya ", - /* $99 */ "sta $%04x,y", - /* $9A */ "txs ", - /* $9B */ "nop ", - /* $9C */ "stz $%04x", - /* $9D */ "sta $%04x,x", - /* $9E */ "stz $%04x,x", - /* $9F */ "bbs1 $%02x, $%04x", - - // $AX - /* $A0 */ "ldy #$%%0%hhux", - /* $A1 */ "lda ($%02x,x)", - /* $A2 */ "ldx #$%%0%hhux", - /* $A3 */ "nop ", - /* $A4 */ "ldy $%02x", - /* $A5 */ "lda $%02x", - /* $A6 */ "ldx $%02x", - /* $A7 */ "smb2 $%02x", - /* $A8 */ "tay ", - /* $A9 */ "lda #$%%0%hhux", - /* $AA */ "tax ", - /* $AB */ "nop ", - /* $AC */ "ldy $%04x", - /* $AD */ "lda $%04x", - /* $AE */ "ldx $%04x", - /* $AF */ "bbs2 $%02x, $%04x", - - // $BX - /* $B0 */ "bcs $%02x", - /* $B1 */ "lda ($%02x),y", - /* $B2 */ "lda ($%02x)", - /* $B3 */ "nop ", - /* $B4 */ "ldy $%02x,x", - /* $B5 */ "lda $%02x,x", - /* $B6 */ "ldx $%02x,y", - /* $B7 */ "smb3 $%02x", - /* $B8 */ "clv ", - /* $B9 */ "lda $%04x,y", - /* $BA */ "tsx ", - /* $BB */ "nop ", - /* $BC */ "ldy $%04x,x", - /* $BD */ "lda $%04x,x", - /* $BE */ "ldx $%04x,y", - /* $BF */ "bbs3 $%02x, $%04x", - - // $CX - /* $C0 */ "cpy #$%%0%hhux", - /* $C1 */ "cmp ($%02x,x)", - /* $C2 */ "nop #$%02x", - /* $C3 */ "nop ", - /* $C4 */ "cpy $%02x", - /* $C5 */ "cmp $%02x", - /* $C6 */ "dec $%02x", - /* $C7 */ "smb4 $%02x", - /* $C8 */ "iny ", - /* $C9 */ "cmp #$%%0%hhux", - /* $CA */ "dex ", - /* $CB */ "wai ", - /* $CC */ "cpy $%04x", - /* $CD */ "cmp $%04x", - /* $CE */ "dec $%04x", - /* $CF */ "bbs4 $%02x, $%04x", - - // $DX - /* $D0 */ "bne $%02x", - /* $D1 */ "cmp ($%02x),y", - /* $D2 */ "cmp ($%02x)", - /* $D3 */ "nop ", - /* $D4 */ "nop #$%02x", - /* $D5 */ "cmp $%02x,x", - /* $D6 */ "dec $%02x,x", - /* $D7 */ "smb5 $%02x", - /* $D8 */ "cld ", - /* $D9 */ "cmp $%04x,y", - /* $DA */ "phx ", - /* $DB */ "dbg ", - /* $DC */ "nop ", - /* $DD */ "cmp $%04x,x", - /* $DE */ "dec $%04x,x", - /* $DF */ "bbs5 $%02x, $%04x", - - // $EX - /* $E0 */ "cpx #$%%0%hhux", - /* $E1 */ "sbc ($%02x,x)", - /* $E2 */ "nop #$%02x", - /* $E3 */ "nop ", - /* $E4 */ "cpx $%02x", - /* $E5 */ "sbc $%02x", - /* $E6 */ "inc $%02x", - /* $E7 */ "smb6 $%02x", - /* $E8 */ "inx ", - /* $E9 */ "sbc #$%%0%hhux", - /* $EA */ "nop ", - /* $EB */ "nop ", - /* $EC */ "cpx $%04x", - /* $ED */ "sbc $%04x", - /* $EE */ "inc $%04x", - /* $EF */ "bbs6 $%02x, $%04x", - - // $FX - /* $F0 */ "beq $%02x", - /* $F1 */ "sbc ($%02x),y", - /* $F2 */ "sbc ($%02x)", - /* $F3 */ "nop ", - /* $F4 */ "nop #$%02x", - /* $F5 */ "sbc $%02x,x", - /* $F6 */ "inc $%02x,x", - /* $F7 */ "smb7 $%02x", - /* $F8 */ "sed ", - /* $F9 */ "sbc $%04x,y", - /* $FA */ "plx ", - /* $FB */ "nop ", - /* $FC */ "nop ", - /* $FD */ "sbc $%04x,x", - /* $FE */ "inc $%04x,x", - /* $FF */ "bbs7 $%02x, $%04x"}; - -static const char *mnemonics_c816[256] = { - // $0X - /* $00 */ "brk #$%02x", - /* $01 */ "ora ($%02x,x)", - /* $02 */ "cop #$%02x", - /* $03 */ "ora $%02x,S", - /* $04 */ "tsb $%02x", - /* $05 */ "ora $%02x", - /* $06 */ "asl $%02x", - /* $07 */ "ora [$%02x]", - /* $08 */ "php ", - /* $09 */ "ora #$%%0%hhux", - /* $0A */ "asl a", - /* $0B */ "phd ", - /* $0C */ "tsb $%04x", - /* $0D */ "ora $%04x", - /* $0E */ "asl $%04x", - /* $0F */ "ora $%06x", - - // $1X - /* $10 */ "bpl $%02x", - /* $11 */ "ora ($%02x),y", - /* $12 */ "ora ($%02x)", - /* $13 */ "ora ($%02x,S),y", - /* $14 */ "trb $%02x", - /* $15 */ "ora $%02x,x", - /* $16 */ "asl $%02x,x", - /* $17 */ "ora [$%02x],y", - /* $18 */ "clc ", - /* $19 */ "ora $%04x,y", - /* $1A */ "inc a", - /* $1B */ "tcs ", - /* $1C */ "trb $%04x", - /* $1D */ "ora $%04x,x", - /* $1E */ "asl $%04x,x", - /* $1F */ "ora $%06x,x", - - // $2X - /* $20 */ "jsr $%04x", - /* $21 */ "and ($%02x,x)", - /* $22 */ "jsl $%06x", - /* $23 */ "and $%02x,S", - /* $24 */ "bit $%02x", - /* $25 */ "and $%02x", - /* $26 */ "rol $%02x", - /* $27 */ "and [$%02x]", - /* $28 */ "plp ", - /* $29 */ "and #$%%0%hhux", - /* $2A */ "rol a", - /* $2B */ "pld ", - /* $2C */ "bit $%04x", - /* $2D */ "and $%04x", - /* $2E */ "rol $%04x", - /* $2F */ "and $%06x", - - // $3X - /* $30 */ "bmi $%02x", - /* $31 */ "and ($%02x),y", - /* $32 */ "and ($%02x)", - /* $33 */ "and ($%02x,S),y", - /* $34 */ "bit $%02x,x", - /* $35 */ "and $%02x,x", - /* $36 */ "rol $%02x,x", - /* $37 */ "and [$%02x],y", - /* $38 */ "sec ", - /* $39 */ "and $%04x,y", - /* $3A */ "dec a", - /* $3B */ "tsc ", - /* $3C */ "bit $%04x,x", - /* $3D */ "and $%04x,x", - /* $3E */ "rol $%04x,x", - /* $3F */ "and $%06x,x", - - // $4X - /* $40 */ "rti ", - /* $41 */ "eor ($%02x,x)", - /* $42 */ "wdm #$%02x", - /* $43 */ "eor $%02x,S", - /* $44 */ "mvp $%02x,$%02x", - /* $45 */ "eor $%02x", - /* $46 */ "lsr $%02x", - /* $47 */ "eor [$%02x]", - /* $48 */ "pha ", - /* $49 */ "eor #$%%0%hhux", - /* $4A */ "lsr a", - /* $4B */ "phk ", - /* $4C */ "jmp $%04x", - /* $4D */ "eor $%04x", - /* $4E */ "lsr $%04x", - /* $4F */ "eor $%06x", - - // $5X - /* $50 */ "bvc $%02x", - /* $51 */ "eor ($%02x),y", - /* $52 */ "eor ($%02x)", - /* $53 */ "eor ($%02x,S),y", - /* $54 */ "mvn $%02x,$%02x", - /* $55 */ "eor $%02x,x", - /* $56 */ "lsr $%02x,x", - /* $57 */ "eor [$%02x],y", - /* $58 */ "cli ", - /* $59 */ "eor $%04x,y", - /* $5A */ "phy ", - /* $5B */ "tcd ", - /* $5C */ "jml $%06x", - /* $5D */ "eor $%04x,x", - /* $5E */ "lsr $%04x,x", - /* $5F */ "eor $%06x,x", - - // $6X - /* $60 */ "rts ", - /* $61 */ "adc ($%02x,x)", - /* $62 */ "per $%04x", - /* $63 */ "adc $%02x,S", - /* $64 */ "stz $%02x", - /* $65 */ "adc $%02x", - /* $66 */ "ror $%02x", - /* $67 */ "adc [$%02x]", - /* $68 */ "pla ", - /* $69 */ "adc #$%%0%hhux", - /* $6A */ "ror a", - /* $6B */ "rtl ", - /* $6C */ "jmp ($%04x)", - /* $6D */ "adc $%04x", - /* $6E */ "ror $%04x", - /* $6F */ "adc $%06x", - - // $7X - /* $70 */ "bvs $%02x", - /* $71 */ "adc ($%02x),y", - /* $72 */ "adc ($%02x)", - /* $73 */ "adc ($%02x,S),y", - /* $74 */ "stz $%02x,x", - /* $75 */ "adc $%02x,x", - /* $76 */ "ror $%02x,x", - /* $77 */ "adc [$%02x],y", - /* $78 */ "sei ", - /* $79 */ "adc $%04x,y", - /* $7A */ "ply ", - /* $7B */ "tdc ", - /* $7C */ "jmp ($%04x,x)", - /* $7D */ "adc $%04x,x", - /* $7E */ "ror $%04x,x", - /* $7F */ "adc $%06x,x", - - // $8X - /* $80 */ "bra $%02x", - /* $81 */ "sta ($%02x,x)", - /* $82 */ "brl $%04x", - /* $83 */ "sta $%02x,S", - /* $84 */ "sty $%02x", - /* $85 */ "sta $%02x", - /* $86 */ "stx $%02x", - /* $87 */ "sta [$%02x]", - /* $88 */ "dey ", - /* $89 */ "bit #$%%0%hhux", - /* $8A */ "txa ", - /* $8B */ "phb ", - /* $8C */ "sty $%04x", - /* $8D */ "sta $%04x", - /* $8E */ "stx $%04x", - /* $8F */ "sta $%06x", - - // $9X - /* $90 */ "bcc $%02x", - /* $91 */ "sta ($%02x),y", - /* $92 */ "sta ($%02x)", - /* $93 */ "sta ($%02x,S),y", - /* $94 */ "sty $%02x,x", - /* $95 */ "sta $%02x,x", - /* $96 */ "stx $%02x,y", - /* $97 */ "sta [$%02x],y", - /* $98 */ "tya ", - /* $99 */ "sta $%04x,y", - /* $9A */ "txs ", - /* $9B */ "txy ", - /* $9C */ "stz $%04x", - /* $9D */ "sta $%04x,x", - /* $9E */ "stz $%04x,x", - /* $9F */ "sta $%06x,x", - - // $AX - /* $A0 */ "ldy #$%%0%hhux", - /* $A1 */ "lda ($%02x,x)", - /* $A2 */ "ldx #$%%0%hhux", - /* $A3 */ "lda $%02x,S", - /* $A4 */ "ldy $%02x", - /* $A5 */ "lda $%02x", - /* $A6 */ "ldx $%02x", - /* $A7 */ "lda [$%02x]", - /* $A8 */ "tay ", - /* $A9 */ "lda #$%%0%hhux", - /* $AA */ "tax ", - /* $AB */ "plb ", - /* $AC */ "ldy $%04x", - /* $AD */ "lda $%04x", - /* $AE */ "ldx $%04x", - /* $AF */ "lda $%06x", - - // $BX - /* $B0 */ "bcs $%02x", - /* $B1 */ "lda ($%02x),y", - /* $B2 */ "lda ($%02x)", - /* $B3 */ "lda ($%02x,S),y", - /* $B4 */ "ldy $%02x,x", - /* $B5 */ "lda $%02x,x", - /* $B6 */ "ldx $%02x,y", - /* $B7 */ "lda [$%02x],y", - /* $B8 */ "clv ", - /* $B9 */ "lda $%04x,y", - /* $BA */ "tsx ", - /* $BB */ "tyx ", - /* $BC */ "ldy $%04x,x", - /* $BD */ "lda $%04x,x", - /* $BE */ "ldx $%04x,y", - /* $BF */ "lda $%06x,x", - - // $CX - /* $C0 */ "cpy #$%%0%hhux", - /* $C1 */ "cmp ($%02x,x)", - /* $C2 */ "rep #$%02x", - /* $C3 */ "cmp $%02x,S", - /* $C4 */ "cpy $%02x", - /* $C5 */ "cmp $%02x", - /* $C6 */ "dec $%02x", - /* $C7 */ "cmp [$%02x]", - /* $C8 */ "iny ", - /* $C9 */ "cmp #$%%0%hhux", - /* $CA */ "dex ", - /* $CB */ "wai ", - /* $CC */ "cpy $%04x", - /* $CD */ "cmp $%04x", - /* $CE */ "dec $%04x", - /* $CF */ "cmp $%06x", - - // $DX - /* $D0 */ "bne $%02x", - /* $D1 */ "cmp ($%02x),y", - /* $D2 */ "cmp ($%02x)", - /* $D3 */ "cmp ($%02x,S),y", - /* $D4 */ "pei ($%02x)", - /* $D5 */ "cmp $%02x,x", - /* $D6 */ "dec $%02x,x", - /* $D7 */ "cmp [$%02x],y", - /* $D8 */ "cld ", - /* $D9 */ "cmp $%04x,y", - /* $DA */ "phx ", - /* $DB */ "dbg ", - /* $DC */ "jml [$%04x]", - /* $DD */ "cmp $%04x,x", - /* $DE */ "dec $%04x,x", - /* $DF */ "cmp $%06x,x", - - // $EX - /* $E0 */ "cpx #$%%0%hhux", - /* $E1 */ "sbc ($%02x,x)", - /* $E2 */ "sep #$%02x", - /* $E3 */ "sbc $%02x,S", - /* $E4 */ "cpx $%02x", - /* $E5 */ "sbc $%02x", - /* $E6 */ "inc $%02x", - /* $E7 */ "sbc [$%02x]", - /* $E8 */ "inx ", - /* $E9 */ "sbc #$%%0%hhux", - /* $EA */ "nop ", - /* $EB */ "xba ", - /* $EC */ "cpx $%04x", - /* $ED */ "sbc $%04x", - /* $EE */ "inc $%04x", - /* $EF */ "sbc $%06x", - - // $FX - /* $F0 */ "beq $%02x", - /* $F1 */ "sbc ($%02x),y", - /* $F2 */ "sbc ($%02x)", - /* $F3 */ "sbc ($%02x,S),y", - /* $F4 */ "pea #$%04x", - /* $F5 */ "sbc $%02x,x", - /* $F6 */ "inc $%02x,x", - /* $F7 */ "sbc [$%02x],y", - /* $F8 */ "sed ", - /* $F9 */ "sbc $%04x,y", - /* $FA */ "plx ", - /* $FB */ "xce ", - /* $FC */ "jsr ($%04x,x)", - /* $FD */ "sbc $%04x,x", - /* $FE */ "inc $%04x,x", - /* $FF */ "sbc $%06x,x"}; diff --git a/src/cpu/tables.h b/src/cpu/tables.h deleted file mode 100644 index aacf120a..00000000 --- a/src/cpu/tables.h +++ /dev/null @@ -1,121 +0,0 @@ -/* Generated by buildtables.py */ - -static void (*addrtable_c02[256])() = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ imp8, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 0 */ -/* 1 */ rel, indy, ind0, imp, zp, zpx, zpx, zp, imp, absy, acc, imp, abso, absx, absx,zprel, /* 1 */ -/* 2 */ abso, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 2 */ -/* 3 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, acc, imp, absx, absx, absx,zprel, /* 3 */ -/* 4 */ imp, indx, imm8, imp, imm8, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 4 */ -/* 5 */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* 5 */ -/* 6 */ imp, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, ind, abso, abso,zprel, /* 6 */ -/* 7 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, imp, imp, ainx, absx, absx,zprel, /* 7 */ -/* 8 */ rel, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* 8 */ -/* 9 */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, abso, absx, absx,zprel, /* 9 */ -/* A */ immx, indx, immx, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* A */ -/* B */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, absx, absx, absy,zprel, /* B */ -/* C */ immx, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* C */ -/* D */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* D */ -/* E */ immx, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* E */ -/* F */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel /* F */ -}; - -static void (*addrtable_c816[256])() = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ imp8, indx, imp8, sr, zp, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 0 */ -/* 1 */ rel, indy, ind0,sridy, zp, zpx, zpx,indly, imp, absy, acc, imp, abso, absx, absx,abslx, /* 1 */ -/* 2 */ abso, indx, absl, sr, zp, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 2 */ -/* 3 */ rel, indy, ind0,sridy, zpx, zpx, zpx,indly, imp, absy, acc, imp, absx, absx, absx,abslx, /* 3 */ -/* 4 */ imp, indx, imm8, sr, bmv, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 4 */ -/* 5 */ rel, indy, ind0,sridy, bmv, zpx, zpx,indly, imp, absy, imp, imp, absl, absx, absx,abslx, /* 5 */ -/* 6 */ imp, indx,rel16, sr, zp, zp, zp,indl0, imp, immm, acc, imp, ind, abso, abso, absl, /* 6 */ -/* 7 */ rel, indy, ind0,sridy, zpx, zpx, zpx,indly, imp, absy, imp, imp, ainx, absx, absx,abslx, /* 7 */ -/* 8 */ rel, indx,rel16, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* 8 */ -/* 9 */ rel, indy, ind0,sridy, zpx, zpx, zpy,indly, imp, absy, imp, imp, abso, absx, absx,abslx, /* 9 */ -/* A */ immx, indx, immx, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* A */ -/* B */ rel, indy, ind0,sridy, zpx, zpx, zpy,indly, imp, absy, imp, imp, absx, absx, absy,abslx, /* B */ -/* C */ immx, indx, imm8, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* C */ -/* D */ rel, indy, ind0,sridy,ind0p, zpx, zpx,indly, imp, absy, imp, imp,aindl, absx, absx,abslx, /* D */ -/* E */ immx, indx, imm8, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* E */ -/* F */ rel, indy, ind0,sridy,imm16, zpx, zpx,indly, imp, absy, imp, imp, ainx, absx, absx,abslx /* F */ -}; - -static void (*optable_c02[256])() = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ brk, ora, nop, nop, tsb, ora, asl, rmb0, php, ora, asl, nop, tsb, ora, asl, bbr0, /* 0 */ -/* 1 */ bpl, ora, ora, nop, trb, ora, asl, rmb1, clc, ora, inc, nop, trb, ora, asl, bbr1, /* 1 */ -/* 2 */ jsr, and, nop, nop, bit, and, rol, rmb2, plp, and, rol, nop, bit, and, rol, bbr2, /* 2 */ -/* 3 */ bmi, and, and, nop, bit, and, rol, rmb3, sec, and, dec, nop, bit, and, rol, bbr3, /* 3 */ -/* 4 */ rti, eor, nop, nop, nop, eor, lsr, rmb4, pha, eor, lsr, nop, jmp, eor, lsr, bbr4, /* 4 */ -/* 5 */ bvc, eor, eor, nop, nop, eor, lsr, rmb5, cli, eor, phy, nop, nop, eor, lsr, bbr5, /* 5 */ -/* 6 */ rts, adc, nop, nop, stz, adc, ror, rmb6, pla, adc, ror, nop, jmp, adc, ror, bbr6, /* 6 */ -/* 7 */ bvs, adc, adc, nop, stz, adc, ror, rmb7, sei, adc, ply, nop, jmp, adc, ror, bbr7, /* 7 */ -/* 8 */ bra, sta, nop, nop, sty, sta, stx, smb0, dey, bit, txa, nop, sty, sta, stx, bbs0, /* 8 */ -/* 9 */ bcc, sta, sta, nop, sty, sta, stx, smb1, tya, sta, txs, nop, stz, sta, stz, bbs1, /* 9 */ -/* A */ ldy, lda, ldx, nop, ldy, lda, ldx, smb2, tay, lda, tax, nop, ldy, lda, ldx, bbs2, /* A */ -/* B */ bcs, lda, lda, nop, ldy, lda, ldx, smb3, clv, lda, tsx, nop, ldy, lda, ldx, bbs3, /* B */ -/* C */ cpy, cmp, nop, nop, cpy, cmp, dec, smb4, iny, cmp, dex, wai, cpy, cmp, dec, bbs4, /* C */ -/* D */ bne, cmp, cmp, nop, nop, cmp, dec, smb5, cld, cmp, phx, dbg, nop, cmp, dec, bbs5, /* D */ -/* E */ cpx, sbc, nop, nop, cpx, sbc, inc, smb6, inx, sbc, nop, nop, cpx, sbc, inc, bbs6, /* E */ -/* F */ beq, sbc, sbc, nop, nop, sbc, inc, smb7, sed, sbc, plx, nop, nop, sbc, inc, bbs7 /* F */ -}; - -static void (*optable_c816[256])() = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ brk, ora, cop, ora, tsb, ora, asl, ora, php, ora, asl, phd, tsb, ora, asl, ora, /* 0 */ -/* 1 */ bpl, ora, ora, ora, trb, ora, asl, ora, clc, ora, inc, tcs, trb, ora, asl, ora, /* 1 */ -/* 2 */ jsr, and, jsl, and, bit, and, rol, and, plp, and, rol, pld, bit, and, rol, and, /* 2 */ -/* 3 */ bmi, and, and, and, bit, and, rol, and, sec, and, dec, tsc, bit, and, rol, and, /* 3 */ -/* 4 */ rti, eor, wdm, eor, mvp, eor, lsr, eor, pha, eor, lsr, phk, jmp, eor, lsr, eor, /* 4 */ -/* 5 */ bvc, eor, eor, eor, mvn, eor, lsr, eor, cli, eor, phy, tcd, jml, eor, lsr, eor, /* 5 */ -/* 6 */ rts, adc, per, adc, stz, adc, ror, adc, pla, adc, ror, rtl, jmp, adc, ror, adc, /* 6 */ -/* 7 */ bvs, adc, adc, adc, stz, adc, ror, adc, sei, adc, ply, tdc, jmp, adc, ror, adc, /* 7 */ -/* 8 */ bra, sta, brl, sta, sty, sta, stx, sta, dey, bit, txa, phb, sty, sta, stx, sta, /* 8 */ -/* 9 */ bcc, sta, sta, sta, sty, sta, stx, sta, tya, sta, txs, txy, stz, sta, stz, sta, /* 9 */ -/* A */ ldy, lda, ldx, lda, ldy, lda, ldx, lda, tay, lda, tax, plb, ldy, lda, ldx, lda, /* A */ -/* B */ bcs, lda, lda, lda, ldy, lda, ldx, lda, clv, lda, tsx, tyx, ldy, lda, ldx, lda, /* B */ -/* C */ cpy, cmp, rep, cmp, cpy, cmp, dec, cmp, iny, cmp, dex, wai, cpy, cmp, dec, cmp, /* C */ -/* D */ bne, cmp, cmp, cmp, pei, cmp, dec, cmp, cld, cmp, phx, dbg, jml, cmp, dec, cmp, /* D */ -/* E */ cpx, sbc, sep, sbc, cpx, sbc, inc, sbc, inx, sbc, nop, xba, cpx, sbc, inc, sbc, /* E */ -/* F */ beq, sbc, sbc, sbc, pea, sbc, inc, sbc, sed, sbc, plx, xce, jsr, sbc, inc, sbc /* F */ -}; - -static const uint32_t ticktable_c02[256] = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ 7, 6, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 5, /* 0 */ -/* 1 */ 2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 7, 5, /* 1 */ -/* 2 */ 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 5, /* 2 */ -/* 3 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 7, 5, /* 3 */ -/* 4 */ 6, 6, 2, 1, 3, 3, 5, 5, 3, 2, 2, 1, 3, 4, 6, 5, /* 4 */ -/* 5 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 7, 5, /* 5 */ -/* 6 */ 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 5, 4, 6, 5, /* 6 */ -/* 7 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 6, 4, 7, 5, /* 7 */ -/* 8 */ 3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, /* 8 */ -/* 9 */ 2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 5, /* 9 */ -/* A */ 2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, /* A */ -/* B */ 2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 5, /* B */ -/* C */ 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, /* C */ -/* D */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 4, 4, 7, 5, /* D */ -/* E */ 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 5, /* E */ -/* F */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 7, 5 /* F */ -}; - -static const uint32_t ticktable_c816[256] = { -/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ -/* 0 */ 7, 6, 7, 4, 5, 3, 5, 6, 3, 2, 2, 4, 6, 4, 6, 5, /* 0 */ -/* 1 */ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 2, 2, 6, 4, 7, 5, /* 1 */ -/* 2 */ 6, 6, 8, 4, 3, 3, 5, 6, 4, 2, 2, 5, 4, 4, 6, 5, /* 2 */ -/* 3 */ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 2, 2, 4, 4, 7, 5, /* 3 */ -/* 4 */ 6, 6, 2, 4, 7, 3, 5, 6, 3, 2, 2, 3, 3, 4, 6, 5, /* 4 */ -/* 5 */ 2, 5, 5, 7, 7, 4, 6, 6, 2, 4, 3, 2, 4, 4, 7, 5, /* 5 */ -/* 6 */ 6, 6, 6, 4, 3, 3, 5, 6, 4, 2, 2, 6, 5, 4, 6, 5, /* 6 */ -/* 7 */ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 4, 2, 6, 4, 7, 5, /* 7 */ -/* 8 */ 3, 6, 4, 4, 3, 3, 3, 6, 2, 2, 2, 3, 4, 4, 4, 5, /* 8 */ -/* 9 */ 2, 6, 5, 7, 4, 4, 4, 6, 2, 5, 2, 2, 4, 5, 5, 5, /* 9 */ -/* A */ 2, 6, 2, 4, 3, 3, 3, 6, 2, 2, 2, 4, 4, 4, 4, 5, /* A */ -/* B */ 2, 5, 5, 7, 4, 4, 4, 6, 2, 4, 2, 2, 4, 4, 4, 5, /* B */ -/* C */ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, /* C */ -/* D */ 2, 5, 5, 7, 6, 4, 6, 6, 2, 4, 3, 1, 6, 4, 7, 5, /* D */ -/* E */ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, /* E */ -/* F */ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 4, 2, 8, 4, 7, 5 /* F */ -}; diff --git a/src/extern/serialuart/serialib/serialib.cpp b/src/extern/serialuart/serialib/serialib.cpp new file mode 100644 index 00000000..8b7e1764 --- /dev/null +++ b/src/extern/serialuart/serialib/serialib.cpp @@ -0,0 +1,1149 @@ +/*! + \file serialib.cpp + \brief Source file of the class serialib. This class is used for communication over a serial device. + \author Philippe Lucidarme (University of Angers) + \version 2.0 + \date december the 27th of 2019 + \modified September 18th of 2025, by Jason Hill + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +This is a licence-free software, it can be used by anyone who try to build a better world. + */ + +#include "serialib.h" + + + +//_____________________________________ +// ::: Constructors and destructors ::: + + +/*! + \brief Constructor of the class serialib. +*/ +serialib::serialib() +{ +#if defined (_WIN32) || defined( _WIN64) + // Set default value for RTS and DTR (Windows only) + currentStateRTS=true; + currentStateDTR=true; + hSerial = INVALID_HANDLE_VALUE; +#endif +#if defined (__linux__) || defined(__APPLE__) + fd = -1; +#endif +} + + +/*! + \brief Destructor of the class serialib. It close the connection +*/ +// Class desctructor +serialib::~serialib() +{ + closeDevice(); +} + + + +//_________________________________________ +// ::: Configuration and initialization ::: + + + +/*! + \brief Open the serial port + \param Device : Port name (COM1, COM2, ... for Windows ) or (/dev/ttyS0, /dev/ttyACM0, /dev/ttyUSB0 ... for linux) + \param Bauds : Baud rate of the serial port. + + \n Supported baud rate for Windows : + - 110 + - 300 + - 600 + - 1200 + - 2400 + - 4800 + - 9600 + - 14400 + - 19200 + - 38400 + - 56000 + - 57600 + - 115200 + - 128000 + - 256000 + + \n Supported baud rate for Linux :\n + - 110 + - 300 + - 600 + - 1200 + - 2400 + - 4800 + - 9600 + - 19200 + - 38400 + - 57600 + - 115200 + + \n Optionally supported baud rates, depending on Linux kernel:\n + - 230400 + - 460800 + - 500000 + - 576000 + - 921600 + - 1000000 + - 1152000 + - 1500000 + - 2000000 + - 2500000 + - 3000000 + - 3500000 + - 4000000 + + \param Databits : Number of data bits in one UART transmission. + + \n Supported values: \n + - SERIAL_DATABITS_5 (5) + - SERIAL_DATABITS_6 (6) + - SERIAL_DATABITS_7 (7) + - SERIAL_DATABITS_8 (8) + - SERIAL_DATABITS_16 (16) (not supported on Unix) + + \param Parity: Parity type + + \n Supported values: \n + - SERIAL_PARITY_NONE (N) + - SERIAL_PARITY_EVEN (E) + - SERIAL_PARITY_ODD (O) + - SERIAL_PARITY_MARK (MARK) (not supported on Unix) + - SERIAL_PARITY_SPACE (SPACE) (not supported on Unix) + \param Stopbit: Number of stop bits + + \n Supported values: + - SERIAL_STOPBITS_1 (1) + - SERIAL_STOPBITS_1_5 (1.5) (not supported on Unix) + - SERIAL_STOPBITS_2 (2) + + \return 1 success + \return -1 device not found + \return -2 error while opening the device + \return -3 error while getting port parameters + \return -4 Speed (Bauds) not recognized + \return -5 error while writing port parameters + \return -6 error while writing timeout parameters + \return -7 Databits not recognized + \return -8 Stopbits not recognized + \return -9 Parity not recognized + */ +int serialib::openDevice(const char *Device, const unsigned int Bauds, + SerialDataBits Databits, + SerialParity Parity, + SerialStopBits Stopbits) { +#if defined (_WIN32) || defined( _WIN64) + // Open serial port + hSerial = CreateFileA(Device,GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,/*FILE_ATTRIBUTE_NORMAL*/0,0); + if(hSerial==INVALID_HANDLE_VALUE) { + if(GetLastError()==ERROR_FILE_NOT_FOUND) + return -1; // Device not found + + // Error while opening the device + return -2; + } + + // Set parameters + + // Structure for the port parameters + DCB dcbSerialParams; + dcbSerialParams.DCBlength=sizeof(dcbSerialParams); + + // Get the port parameters + if (!GetCommState(hSerial, &dcbSerialParams)) return -3; + + // Set the speed (Bauds) + switch (Bauds) + { + case 110 : dcbSerialParams.BaudRate=CBR_110; break; + case 300 : dcbSerialParams.BaudRate=CBR_300; break; + case 600 : dcbSerialParams.BaudRate=CBR_600; break; + case 1200 : dcbSerialParams.BaudRate=CBR_1200; break; + case 2400 : dcbSerialParams.BaudRate=CBR_2400; break; + case 4800 : dcbSerialParams.BaudRate=CBR_4800; break; + case 9600 : dcbSerialParams.BaudRate=CBR_9600; break; + case 14400 : dcbSerialParams.BaudRate=CBR_14400; break; + case 19200 : dcbSerialParams.BaudRate=CBR_19200; break; + case 38400 : dcbSerialParams.BaudRate=CBR_38400; break; + case 56000 : dcbSerialParams.BaudRate=CBR_56000; break; + case 57600 : dcbSerialParams.BaudRate=CBR_57600; break; + case 115200 : dcbSerialParams.BaudRate=CBR_115200; break; + case 128000 : dcbSerialParams.BaudRate=CBR_128000; break; + case 256000 : dcbSerialParams.BaudRate=CBR_256000; break; + default : return -4; + } + //select data size + BYTE bytesize = 0; + switch(Databits) { + case SERIAL_DATABITS_5: bytesize = 5; break; + case SERIAL_DATABITS_6: bytesize = 6; break; + case SERIAL_DATABITS_7: bytesize = 7; break; + case SERIAL_DATABITS_8: bytesize = 8; break; + case SERIAL_DATABITS_16: bytesize = 16; break; + default: return -7; + } + BYTE stopBits = 0; + switch(Stopbits) { + case SERIAL_STOPBITS_1: stopBits = ONESTOPBIT; break; + case SERIAL_STOPBITS_1_5: stopBits = ONE5STOPBITS; break; + case SERIAL_STOPBITS_2: stopBits = TWOSTOPBITS; break; + default: return -8; + } + BYTE parity = 0; + switch(Parity) { + case SERIAL_PARITY_NONE: parity = NOPARITY; break; + case SERIAL_PARITY_EVEN: parity = EVENPARITY; break; + case SERIAL_PARITY_ODD: parity = ODDPARITY; break; + case SERIAL_PARITY_MARK: parity = MARKPARITY; break; + case SERIAL_PARITY_SPACE: parity = SPACEPARITY; break; + default: return -9; + } + // configure byte size + dcbSerialParams.ByteSize = bytesize; + // configure stop bits + dcbSerialParams.StopBits = stopBits; + // configure parity + dcbSerialParams.Parity = parity; + + // Write the parameters + if(!SetCommState(hSerial, &dcbSerialParams)) return -5; + + // Set TimeOut + + // Set the Timeout parameters + timeouts.ReadIntervalTimeout=0; + // No TimeOut + timeouts.ReadTotalTimeoutConstant=MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier=0; + timeouts.WriteTotalTimeoutConstant=MAXDWORD; + timeouts.WriteTotalTimeoutMultiplier=0; + + // Write the parameters + if(!SetCommTimeouts(hSerial, &timeouts)) return -6; + + // Opening successfull + return 1; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Structure with the device's options + struct termios options; + + + // Open device + fd = open(Device, O_RDWR | O_NOCTTY | O_NDELAY); + // If the device is not open, return -2 + if (fd == -1) return -2; + // Open the device in nonblocking mode + fcntl(fd, F_SETFL, FNDELAY); + + + // Get the current options of the port + tcgetattr(fd, &options); + // Clear all the options + bzero(&options, sizeof(options)); + + // Prepare speed (Bauds) + speed_t Speed; + switch (Bauds) + { + case 110 : Speed=B110; break; + case 300 : Speed=B300; break; + case 600 : Speed=B600; break; + case 1200 : Speed=B1200; break; + case 2400 : Speed=B2400; break; + case 4800 : Speed=B4800; break; + case 9600 : Speed=B9600; break; + case 19200 : Speed=B19200; break; + case 38400 : Speed=B38400; break; + case 57600 : Speed=B57600; break; + case 115200 : Speed=B115200; break; +#if defined (B230400) + case 230400 : Speed=B230400; break; +#endif +#if defined (B460800) + case 460800 : Speed=B460800; break; +#endif +#if defined (B500000) + case 500000 : Speed=B500000; break; +#endif +#if defined (B576000) + case 576000 : Speed=B576000; break; +#endif +#if defined (B921600) + case 921600 : Speed=B921600; break; +#endif +#if defined (B1000000) + case 1000000 : Speed=B1000000; break; +#endif +#if defined (B1152000) + case 1152000 : Speed=B1152000; break; +#endif +#if defined (B1500000) + case 1500000 : Speed=B1500000; break; +#endif +#if defined (B2000000) + case 2000000 : Speed=B2000000; break; +#endif +#if defined (B2500000) + case 2500000 : Speed=B2500000; break; +#endif +#if defined (B3000000) + case 3000000 : Speed=B3000000; break; +#endif +#if defined (B3500000) + case 3500000 : Speed=B3500000; break; +#endif +#if defined (B4000000) + case 4000000 : Speed=B4000000; break; +#endif + default : return -4; + } + int databits_flag = 0; + switch(Databits) { + case SERIAL_DATABITS_5: databits_flag = CS5; break; + case SERIAL_DATABITS_6: databits_flag = CS6; break; + case SERIAL_DATABITS_7: databits_flag = CS7; break; + case SERIAL_DATABITS_8: databits_flag = CS8; break; + //16 bits and everything else not supported + default: return -7; + } + int stopbits_flag = 0; + switch(Stopbits) { + case SERIAL_STOPBITS_1: stopbits_flag = 0; break; + case SERIAL_STOPBITS_2: stopbits_flag = CSTOPB; break; + //1.5 stopbits and everything else not supported + default: return -8; + } + int parity_flag = 0; + switch(Parity) { + case SERIAL_PARITY_NONE: parity_flag = 0; break; + case SERIAL_PARITY_EVEN: parity_flag = PARENB; break; + case SERIAL_PARITY_ODD: parity_flag = (PARENB | PARODD); break; + + /* + case SERIAL_PARITY_MARK: parity_flag = (PARENB | PARODD | CMSPAR); break; + case SERIAL_PARITY_SPACE: + parity_flag = (PARENB | CMSPAR); + parity_flag &= ~PARODD; + break; + */// CMSPAR Not supported on MacOS and some Linux distros + + default: return -9; + } + + // Set the baud rate + cfsetispeed(&options, Speed); + cfsetospeed(&options, Speed); + // Configure the device : data bits, stop bits, parity, no control flow + // Ignore modem control lines (CLOCAL) and Enable receiver (CREAD) + options.c_cflag |= ( CLOCAL | CREAD | databits_flag | parity_flag | stopbits_flag); + options.c_iflag |= ( IGNPAR | IGNBRK ); + // Timer unused + options.c_cc[VTIME]=0; + // At least on character before satisfy reading + options.c_cc[VMIN]=0; + // Activate the settings + tcsetattr(fd, TCSANOW, &options); + // Success + return (1); +#endif + +} + +bool serialib::isDeviceOpen() +{ +#if defined (_WIN32) || defined( _WIN64) + return hSerial != INVALID_HANDLE_VALUE; +#endif +#if defined (__linux__) || defined(__APPLE__) + return fd >= 0; +#endif +} + +/*! + \brief Close the connection with the current device +*/ +void serialib::closeDevice() +{ +#if defined (_WIN32) || defined( _WIN64) + CloseHandle(hSerial); + hSerial = INVALID_HANDLE_VALUE; +#endif +#if defined (__linux__) || defined(__APPLE__) + close (fd); + fd = -1; +#endif +} + + + + +//___________________________________________ +// ::: Read/Write operation on characters ::: + + + +/*! + \brief Write a char on the current serial port + \param Byte : char to send on the port (must be terminated by '\0') + \return 1 success + \return -1 error while writting data + */ +int serialib::writeChar(const char Byte) +{ +#if defined (_WIN32) || defined( _WIN64) + // Number of bytes written + DWORD dwBytesWritten; + // Write the char to the serial device + // Return -1 if an error occured + if(!WriteFile(hSerial,&Byte,1,&dwBytesWritten,NULL)) return -1; + // Write operation successfull + return 1; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Write the char + if (write(fd,&Byte,1)!=1) return -1; + + // Write operation successfull + return 1; +#endif +} + + + +//________________________________________ +// ::: Read/Write operation on strings ::: + + +/*! + \brief Write a string on the current serial port + \param receivedString : string to send on the port (must be terminated by '\0') + \return 1 success + \return -1 error while writting data + */ +int serialib::writeString(const char *receivedString) +{ +#if defined (_WIN32) || defined( _WIN64) + // Number of bytes written + DWORD dwBytesWritten; + // Write the string + if(!WriteFile(hSerial,receivedString,strlen(receivedString),&dwBytesWritten,NULL)) + // Error while writing, return -1 + return -1; + // Write operation successfull + return 1; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Lenght of the string + int Lenght=strlen(receivedString); + // Write the string + if (write(fd,receivedString,Lenght)!=Lenght) return -1; + // Write operation successfull + return 1; +#endif +} + +// _____________________________________ +// ::: Read/Write operation on bytes ::: + + + +/*! + \brief Write an array of data on the current serial port + \param Buffer : array of bytes to send on the port + \param NbBytes : number of byte to send + \return 1 success + \return -1 error while writting data + */ +int serialib::writeBytes(const void *Buffer, const unsigned int NbBytes, unsigned int *NbBytesWritten) +{ +#if defined (_WIN32) || defined( _WIN64) + // Write data + DWORD NumBytes; + //if(!WriteFile(hSerial, Buffer, NbBytes, NbBytesWritten, NULL)) + if(!WriteFile(hSerial, Buffer, NbBytes, &NumBytes, NULL)){ + // Error while writing, return -1 + return -1; + } + // Write operation successfull + return 1; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Write data + *NbBytesWritten = write (fd,Buffer,NbBytes); + if (*NbBytesWritten !=(ssize_t)NbBytes) return -1; + // Write operation successfull + return 1; +#endif +} + +int serialib::writeBytes(const void *Buffer, const unsigned int NbBytes) +{ + unsigned int NbBytesWritten; + return writeBytes(Buffer, NbBytes, &NbBytesWritten); +} + +/*! + \brief Wait for a byte from the serial device and return the data read + \param pByte : data read on the serial device + \param timeOut_ms : delay of timeout before giving up the reading + If set to zero, timeout is disable (Optional) + \return 1 success + \return 0 Timeout reached + \return -1 error while setting the Timeout + \return -2 error while reading the byte + */ +int serialib::readChar(char *pByte,unsigned int timeOut_ms) +{ +#if defined (_WIN32) || defined(_WIN64) + // Number of bytes read + DWORD dwBytesRead = 0; + + // Set the TimeOut + timeouts.ReadTotalTimeoutConstant=timeOut_ms; + + // Write the parameters, return -1 if an error occured + if(!SetCommTimeouts(hSerial, &timeouts)) return -1; + + // Read the byte, return -2 if an error occured + if(!ReadFile(hSerial,pByte, 1, &dwBytesRead, NULL)) return -2; + + // Return 0 if the timeout is reached + if (dwBytesRead==0) return 0; + + // The byte is read + return 1; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Timer used for timeout + timeOut timer; + // Initialise the timer + timer.initTimer(); + // While Timeout is not reached + while (timer.elapsedTime_ms()0 success, return the number of bytes read + \return -1 error while setting the Timeout + \return -2 error while reading the byte + \return -3 MaxNbBytes is reached + */ +int serialib::readStringNoTimeOut(char *receivedString,char finalChar,unsigned int maxNbBytes) +{ + // Number of characters read + unsigned int NbBytes=0; + // Returned value from Read + char charRead; + + // While the buffer is not full + while (NbBytes0 success, return the number of bytes read (including the null character) + \return 0 timeout is reached + \return -1 error while setting the Timeout + \return -2 error while reading the character + \return -3 MaxNbBytes is reached + */ +int serialib::readString(char *receivedString,char finalChar,unsigned int maxNbBytes,unsigned int timeOut_ms) +{ + // Check if timeout is requested + if (timeOut_ms==0) return readStringNoTimeOut(receivedString,finalChar,maxNbBytes); + + // Number of bytes read + unsigned int nbBytes=0; + // Character read on serial device + char charRead; + // Timer used for timeout + timeOut timer; + long int timeOutParam; + + // Initialize the timer (for timeout) + timer.initTimer(); + + // While the buffer is not full + while (nbBytes0) + { + // Wait for a byte on the serial link with the remaining time as timeout + charRead=readChar(&receivedString[nbBytes],timeOutParam); + + // If a byte has been received + if (charRead==1) + { + // Check if the character received is the final one + if (receivedString[nbBytes]==finalChar) + { + // Final character: add the end character 0 + receivedString [++nbBytes]=0; + // Return the number of bytes read + return nbBytes; + } + // This is not the final character, just increase the number of bytes read + nbBytes++; + } + // Check if an error occured during reading char + // If an error occurend, return the error number + if (charRead<0) return charRead; + } + // Check if timeout is reached + if (timer.elapsedTime_ms()>timeOut_ms) + { + // Add the end caracter + receivedString[nbBytes]=0; + // Return 0 (timeout reached) + return 0; + } + } + + // Buffer is full : return -3 + return -3; +} + + +/*! + \brief Read an array of bytes from the serial device (with timeout) + \param buffer : array of bytes read from the serial device + \param maxNbBytes : maximum allowed number of bytes read + \param timeOut_ms : delay of timeout before giving up the reading + \param sleepDuration_us : delay of CPU relaxing in microseconds (Linux only) + In the reading loop, a sleep can be performed after each reading + This allows CPU to perform other tasks + \return >=0 return the number of bytes read before timeout or + requested data is completed + \return -1 error while setting the Timeout + \return -2 error while reading the byte + */ +int serialib::readBytes (void *buffer,unsigned int maxNbBytes,unsigned int timeOut_ms, unsigned int sleepDuration_us) +{ +#if defined (_WIN32) || defined(_WIN64) + // Avoid warning while compiling + UNUSED(sleepDuration_us); + + // Number of bytes read + DWORD dwBytesRead = 0; + + // Set the TimeOut + timeouts.ReadTotalTimeoutConstant=(DWORD)timeOut_ms; + + // Write the parameters and return -1 if an error occrured + if(!SetCommTimeouts(hSerial, &timeouts)) return -1; + + + // Read the bytes from the serial device, return -2 if an error occured + if(!ReadFile(hSerial,buffer,(DWORD)maxNbBytes,&dwBytesRead, NULL)) return -2; + + // Return the byte read + return dwBytesRead; +#endif +#if defined (__linux__) || defined(__APPLE__) + // Timer used for timeout + timeOut timer; + // Initialise the timer + timer.initTimer(); + unsigned int NbByteRead=0; + // While Timeout is not reached + while (timer.elapsedTime_ms()0) + { + // Increase the number of read bytes + NbByteRead+=Ret; + // Success : bytes has been read + if (NbByteRead>=maxNbBytes) + return NbByteRead; + } + // Suspend the loop to avoid charging the CPU + usleep (sleepDuration_us); + } + // Timeout reached, return the number of bytes read + return NbByteRead; +#endif +} + + + + +// _________________________ +// ::: Special operation ::: + + + +/*! + \brief Empty receiver buffer + Note that when using serial over USB on Unix systems, a delay of 20ms may be necessary before calling the flushReceiver function + \return If the function succeeds, the return value is nonzero. + If the function fails, the return value is zero. +*/ +char serialib::flushReceiver() +{ +#if defined (_WIN32) || defined(_WIN64) + // Purge receiver + return PurgeComm (hSerial, PURGE_RXCLEAR); +#endif +#if defined (__linux__) || defined(__APPLE__) + // Purge receiver + tcflush(fd,TCIFLUSH); + return true; +#endif +} + + + +/*! + \brief Return the number of bytes in the received buffer (UNIX only) + \return The number of bytes received by the serial provider but not yet read. +*/ +int serialib::available() +{ +#if defined (_WIN32) || defined(_WIN64) + // Device errors + DWORD commErrors; + // Device status + COMSTAT commStatus; + // Read status + ClearCommError(hSerial, &commErrors, &commStatus); + // Return the number of pending bytes + return commStatus.cbInQue; +#endif +#if defined (__linux__) || defined(__APPLE__) + int nBytes=0; + // Return number of pending bytes in the receiver + ioctl(fd, FIONREAD, &nBytes); + return nBytes; +#endif + +} + + + +// __________________ +// ::: I/O Access ::: + +/*! + \brief Set or unset the bit DTR (pin 4) + DTR stands for Data Terminal Ready + Convenience method :This method calls setDTR and clearDTR + \param status = true set DTR + status = false unset DTR + \return If the function fails, the return value is false + If the function succeeds, the return value is true. +*/ +bool serialib::DTR(bool status) +{ + if (status) + // Set DTR + return this->setDTR(); + else + // Unset DTR + return this->clearDTR(); +} + + +/*! + \brief Set the bit DTR (pin 4) + DTR stands for Data Terminal Ready + \return If the function fails, the return value is false + If the function succeeds, the return value is true. +*/ +bool serialib::setDTR() +{ +#if defined (_WIN32) || defined(_WIN64) + // Set DTR + currentStateDTR=true; + return EscapeCommFunction(hSerial,SETDTR); +#endif +#if defined (__linux__) || defined(__APPLE__) + // Set DTR + int status_DTR=0; + ioctl(fd, TIOCMGET, &status_DTR); + status_DTR |= TIOCM_DTR; + ioctl(fd, TIOCMSET, &status_DTR); + return true; +#endif +} + +/*! + \brief Clear the bit DTR (pin 4) + DTR stands for Data Terminal Ready + \return If the function fails, the return value is false + If the function succeeds, the return value is true. +*/ +bool serialib::clearDTR() +{ +#if defined (_WIN32) || defined(_WIN64) + // Clear DTR + currentStateDTR=false; + return EscapeCommFunction(hSerial,CLRDTR); +#endif +#if defined (__linux__) || defined(__APPLE__) + // Clear DTR + int status_DTR=0; + ioctl(fd, TIOCMGET, &status_DTR); + status_DTR &= ~TIOCM_DTR; + ioctl(fd, TIOCMSET, &status_DTR); + return true; +#endif +} + + + +/*! + \brief Set or unset the bit RTS (pin 7) + RTS stands for Data Termina Ready + Convenience method :This method calls setDTR and clearDTR + \param status = true set DTR + status = false unset DTR + \return false if the function fails + \return true if the function succeeds +*/ +bool serialib::RTS(bool status) +{ + if (status) + // Set RTS + return this->setRTS(); + else + // Unset RTS + return this->clearRTS(); +} + + +/*! + \brief Set the bit RTS (pin 7) + RTS stands for Data Terminal Ready + \return If the function fails, the return value is false + If the function succeeds, the return value is true. +*/ +bool serialib::setRTS() +{ +#if defined (_WIN32) || defined(_WIN64) + // Set RTS + currentStateRTS=true; + return EscapeCommFunction(hSerial,SETRTS); +#endif +#if defined (__linux__) || defined(__APPLE__) + // Set RTS + int status_RTS=0; + ioctl(fd, TIOCMGET, &status_RTS); + status_RTS |= TIOCM_RTS; + ioctl(fd, TIOCMSET, &status_RTS); + return true; +#endif +} + + + +/*! + \brief Clear the bit RTS (pin 7) + RTS stands for Data Terminal Ready + \return If the function fails, the return value is false + If the function succeeds, the return value is true. +*/ +bool serialib::clearRTS() +{ +#if defined (_WIN32) || defined(_WIN64) + // Clear RTS + currentStateRTS=false; + return EscapeCommFunction(hSerial,CLRRTS); +#endif +#if defined (__linux__) || defined(__APPLE__) + // Clear RTS + int status_RTS=0; + ioctl(fd, TIOCMGET, &status_RTS); + status_RTS &= ~TIOCM_RTS; + ioctl(fd, TIOCMSET, &status_RTS); + return true; +#endif +} + + + + +/*! + \brief Get the CTS's status (pin 8) + CTS stands for Clear To Send + \return Return true if CTS is set otherwise false + */ +bool serialib::isCTS() +{ +#if defined (_WIN32) || defined(_WIN64) + DWORD modemStat; + GetCommModemStatus(hSerial, &modemStat); + return modemStat & MS_CTS_ON; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the CTS bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_CTS; +#endif +} + + + +/*! + \brief Get the DSR's status (pin 6) + DSR stands for Data Set Ready + \return Return true if DTR is set otherwise false + */ +bool serialib::isDSR() +{ +#if defined (_WIN32) || defined(_WIN64) + DWORD modemStat; + GetCommModemStatus(hSerial, &modemStat); + return modemStat & MS_DSR_ON; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the DSR bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_DSR; +#endif +} + + + + + + +/*! + \brief Get the DCD's status (pin 1) + CDC stands for Data Carrier Detect + \return true if DCD is set + \return false otherwise + */ +bool serialib::isDCD() +{ +#if defined (_WIN32) || defined(_WIN64) + DWORD modemStat; + GetCommModemStatus(hSerial, &modemStat); + return modemStat & MS_RLSD_ON; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the DCD bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_CAR; +#endif +} + + +/*! + \brief Get the RING's status (pin 9) + Ring Indicator + \return Return true if RING is set otherwise false + */ +bool serialib::isRI() +{ +#if defined (_WIN32) || defined(_WIN64) + DWORD modemStat; + GetCommModemStatus(hSerial, &modemStat); + return modemStat & MS_RING_ON; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the RING bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_RNG; +#endif +} + + +/*! + \brief Get the DTR's status (pin 4) + DTR stands for Data Terminal Ready + May behave abnormally on Windows + \return Return true if CTS is set otherwise false + */ +bool serialib::isDTR() +{ +#if defined (_WIN32) || defined( _WIN64) + return currentStateDTR; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the DTR bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_DTR ; +#endif +} + + + +/*! + \brief Get the RTS's status (pin 7) + RTS stands for Request To Send + May behave abnormally on Windows + \return Return true if RTS is set otherwise false + */ +bool serialib::isRTS() +{ +#if defined (_WIN32) || defined(_WIN64) + return currentStateRTS; +#endif +#if defined (__linux__) || defined(__APPLE__) + int status=0; + //Get the current status of the CTS bit + ioctl(fd, TIOCMGET, &status); + return status & TIOCM_RTS; +#endif +} + + + + + + +// ****************************************** +// Class timeOut +// ****************************************** + + +/*! + \brief Constructor of the class timeOut. +*/ +// Constructor +timeOut::timeOut() +{} + + +/*! + \brief Initialise the timer. It writes the current time of the day in the structure PreviousTime. +*/ +//Initialize the timer +void timeOut::initTimer() +{ +#if defined (NO_POSIX_TIME) + LARGE_INTEGER tmp; + QueryPerformanceFrequency(&tmp); + counterFrequency = tmp.QuadPart; + // Used to store the previous time (for computing timeout) + QueryPerformanceCounter(&tmp); + previousTime = tmp.QuadPart; +#else + gettimeofday(&previousTime, NULL); +#endif +} + +/*! + \brief Returns the time elapsed since initialization. It write the current time of the day in the structure CurrentTime. + Then it returns the difference between CurrentTime and PreviousTime. + \return The number of microseconds elapsed since the functions InitTimer was called. + */ +//Return the elapsed time since initialization +unsigned long int timeOut::elapsedTime_ms() +{ +#if defined (NO_POSIX_TIME) + // Current time + LARGE_INTEGER CurrentTime; + // Number of ticks since last call + int sec; + + // Get current time + QueryPerformanceCounter(&CurrentTime); + + // Compute the number of ticks elapsed since last call + sec=CurrentTime.QuadPart-previousTime; + + // Return the elapsed time in milliseconds + return sec/(counterFrequency/1000); +#else + // Current time + struct timeval CurrentTime; + // Number of seconds and microseconds since last call + int sec,usec; + + // Get current time + gettimeofday(&CurrentTime, NULL); + + // Compute the number of seconds and microseconds elapsed since last call + sec=CurrentTime.tv_sec-previousTime.tv_sec; + usec=CurrentTime.tv_usec-previousTime.tv_usec; + + // If the previous usec is higher than the current one + if (usec<0) + { + // Recompute the microseonds and substract one second + usec=1000000-previousTime.tv_usec+CurrentTime.tv_usec; + sec--; + } + + // Return the elapsed time in milliseconds + return sec*1000+usec/1000; +#endif +} diff --git a/src/extern/serialuart/serialib/serialib.h b/src/extern/serialuart/serialib/serialib.h new file mode 100644 index 00000000..99f78aff --- /dev/null +++ b/src/extern/serialuart/serialib/serialib.h @@ -0,0 +1,273 @@ +/*! +\file serialib.h +\brief Header file of the class serialib. This class is used for communication over a serial device. +\author Philippe Lucidarme (University of Angers) +\version 2.0 +\date December the 27th of 2019 +\modified September 18th of 2025, by Jason Hill + +This Serial library is used to communicate through serial port. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This is a licence-free software, it can be used by anyone who try to build a better world. +*/ + + +#ifndef SERIALIB_H +#define SERIALIB_H + +#if defined(__CYGWIN__) + // This is Cygwin special case + #include +#endif + +// Include for windows +#if defined (_WIN32) || defined (_WIN64) +#if defined(__GNUC__) + // This is MinGW special case + #include +#else + // sys/time.h does not exist on "actual" Windows + #define NO_POSIX_TIME +#endif + // Accessing to the serial port under Windows + #include +#endif + +// Include for Linux +#if defined (__linux__) || defined(__APPLE__) + #include + #include + #include + #include + #include + #include + #include + // File control definitions + #include + #include + #include +#endif + +/*! To avoid unused parameters */ +#define UNUSED(x) (void)(x) + +/** + * number of serial data bits + */ +enum SerialDataBits { + SERIAL_DATABITS_5, /**< 5 databits */ + SERIAL_DATABITS_6, /**< 6 databits */ + SERIAL_DATABITS_7, /**< 7 databits */ + SERIAL_DATABITS_8, /**< 8 databits */ + SERIAL_DATABITS_16, /**< 16 databits */ +}; + +/** + * number of serial stop bits + */ +enum SerialStopBits { + SERIAL_STOPBITS_1, /**< 1 stop bit */ + SERIAL_STOPBITS_1_5, /**< 1.5 stop bits */ + SERIAL_STOPBITS_2, /**< 2 stop bits */ +}; + +/** + * type of serial parity bits + */ +enum SerialParity { + SERIAL_PARITY_NONE, /**< no parity bit */ + SERIAL_PARITY_EVEN, /**< even parity bit */ + SERIAL_PARITY_ODD, /**< odd parity bit */ + SERIAL_PARITY_MARK, /**< mark parity */ + SERIAL_PARITY_SPACE /**< space bit */ +}; + +/*! \class serialib + \brief This class is used for communication over a serial device. +*/ +class serialib +{ +public: + + //_____________________________________ + // ::: Constructors and destructors ::: + + + + // Constructor of the class + serialib (); + + // Destructor + ~serialib (); + + + + //_________________________________________ + // ::: Configuration and initialization ::: + + + // Open a device + int openDevice(const char *Device, const unsigned int Bauds, + SerialDataBits Databits = SERIAL_DATABITS_8, + SerialParity Parity = SERIAL_PARITY_NONE, + SerialStopBits Stopbits = SERIAL_STOPBITS_1); + + // Check device opening state + bool isDeviceOpen(); + + // Close the current device + void closeDevice(); + + + + + //___________________________________________ + // ::: Read/Write operation on characters ::: + + + // Write a char + int writeChar (char); + + // Read a char (with timeout) + int readChar (char *pByte,const unsigned int timeOut_ms=0); + + + + + //________________________________________ + // ::: Read/Write operation on strings ::: + + + // Write a string + int writeString (const char *String); + + // Read a string (with timeout) + int readString ( char *receivedString, + char finalChar, + unsigned int maxNbBytes, + const unsigned int timeOut_ms=0); + + + + // _____________________________________ + // ::: Read/Write operation on bytes ::: + + + // Write an array of bytes + int writeBytes(const void *Buffer, const unsigned int NbBytes, unsigned int *NbBytesWritten); + int writeBytes (const void *Buffer, const unsigned int NbBytes); + + // Read an array of byte (with timeout) + int readBytes (void *buffer,unsigned int maxNbBytes,const unsigned int timeOut_ms=0, unsigned int sleepDuration_us=100); + + + + + // _________________________ + // ::: Special operation ::: + + + // Empty the received buffer + char flushReceiver(); + + // Return the number of bytes in the received buffer + int available(); + + + + + // _________________________ + // ::: Access to IO bits ::: + + + // Set CTR status (Data Terminal Ready, pin 4) + bool DTR(bool status); + bool setDTR(); + bool clearDTR(); + + // Set RTS status (Request To Send, pin 7) + bool RTS(bool status); + bool setRTS(); + bool clearRTS(); + + // Get RI status (Ring Indicator, pin 9) + bool isRI(); + + // Get DCD status (Data Carrier Detect, pin 1) + bool isDCD(); + + // Get CTS status (Clear To Send, pin 8) + bool isCTS(); + + // Get DSR status (Data Set Ready, pin 9) + bool isDSR(); + + // Get RTS status (Request To Send, pin 7) + bool isRTS(); + + // Get CTR status (Data Terminal Ready, pin 4) + bool isDTR(); + + +private: + // Read a string (no timeout) + int readStringNoTimeOut (char *String,char FinalChar,unsigned int MaxNbBytes); + +#if defined (_WIN32) || defined(_WIN64) + // Current DTR and RTS state (can't be read on WIndows) + bool currentStateRTS; + bool currentStateDTR; +#endif + + + + +#if defined (_WIN32) || defined( _WIN64) + // Handle on serial device + HANDLE hSerial; + // For setting serial port timeouts + COMMTIMEOUTS timeouts; +#endif +#if defined (__linux__) || defined(__APPLE__) + int fd; +#endif + +}; + + + +/*! \class timeOut + \brief This class can manage a timer which is used as a timeout. + */ +// Class timeOut +class timeOut +{ +public: + + // Constructor + timeOut(); + + // Init the timer + void initTimer(); + + // Return the elapsed time since initialization + unsigned long int elapsedTime_ms(); + +private: +#if defined (NO_POSIX_TIME) + // Used to store the previous time (for computing timeout) + LONGLONG counterFrequency; + LONGLONG previousTime; +#else + // Used to store the previous time (for computing timeout) + struct timeval previousTime; +#endif +}; + +#endif // serialib_H diff --git a/src/extern/serialuart/serialuartTL16C2550.cpp b/src/extern/serialuart/serialuartTL16C2550.cpp new file mode 100644 index 00000000..84a7df30 --- /dev/null +++ b/src/extern/serialuart/serialuartTL16C2550.cpp @@ -0,0 +1,551 @@ +/*! +\file serialuartTL16C2550.cpp +\brief Source file of the class serialuartTL16C2550. A serial UART for Commander X16 Emulator +\author Jason Hill +\version 0.1 +\date September 18th of 2025, by Jason Hill +\modified none + +This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating +some aspects of the TL16C2550 for use on personal computers. + +Description: This file will simulate many functions of the Texas Instruments TL16C2550 UART. Some functions +have been omtited due to arhcitectrual differences between hardware and OS control. Some FIFO functionas +are 'faked' for ease of use. + +This file will emulate one serial port by attaching it to a physical port on the computer. Adjusting port +configurations as necessary based on register settings as they are programmed.It is not a complete +implementation, but will run most programs. + +On hardware, UART1 is connected to an ESP32 on the X16 Serial/Wifi Card. An ESP8266 or ESP32 will +need to be attached to a COM port, with proper firmware, to get the same functionality. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This is a licence-free software, it can be used by anyone who try to build a better world. +*/ + +//#define DEBUGUART +//#define VERBOSE + +#include "serialuartTL16C2550.hpp" + +serialuartTL16C2550::~serialuartTL16C2550(){ + this->serialPort.closeDevice(); +} + +int serialuartTL16C2550::addrwrite(unsigned char* value, int address){ + int retVal = 0; + +#ifdef DEBUGUART //Debug + if(address != 0){ + printf("WriteAddress: $%02x\tValue: $%02x", address,(int)(*value)); + printf("\t-----\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x MSR: $%02x Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); + printf(" Divisor: %d\tBaud: %d\n", requestedDivisor, baudCalculator(requestedDivisor) ); + }//*/ +#endif + bool LCRconfigDirty=false; //This triggers a closing and opening of the serial port. + + switch(address & 0x07){ + case 0: //Transmitter Holding Register + if( LCR & 0x80 /*DLAB=1*/){ + DLSB = *value; + retVal = DLSB; + } + else{ + if( MCR & 0x10 /*Loop back mode*/ ){ + loopvalue=*value; + retVal=1; + } else { + retVal=this->write(value); + } + } + + break; + + case 1: //Interrupt Enable Register + if( LCR & 0x80 /*DLAB=1*/){ + DMSB = *value; + retVal = DMSB; + + } else { + IER = (0x0F & (*value) ); + } + break; + + case 2: //FIFO Control Register + FCR = 0xCF & (*value); + retVal = FCR; + break; + + case 3: //Line Control Register + //Handle SerialPort reconfigurations + if(LCR & 0x80){ //We are in Divisor Latch Mode currently + if( ( (*value) & 0x80 ) == 0 ){ + //We are exiting Divisor Latch Mode; update baud rate + requestedDivisor = DLSB; + requestedDivisor |= ((uint16_t)(DMSB<<8)); + LCRconfigDirty=true; + } + } + + if ( (LCR&0x7F) != ( (*value)&0x7f ) ){ + LCRconfigDirty=true; + } + + LCR=*value; + + if(LCRconfigDirty){ + reconfigureSerial(); + } + retVal = LCR; + break; + + case 4: //Modem Control Register + if(*value != MCR){ + MCR =*value; + reconfigureSerial(); + } + retVal=MCR; + break; + + case 5: //Line Status Register + //LSR is read only + retVal=-5; + break; + + case 6: //Modem Status Register + //MSR is read only + retVal=-6; + break; + + case 7: //Scratch Register + scratchReg = *value; + retVal=0; + break; + + default: + break; + } + return retVal; +} + +int serialuartTL16C2550::addrread(unsigned char* value, int address){ + int retVal = -1; + +#ifdef DEBUGUART //Debug + if(address != 0){ + printf("ReadAddress: $%02x", address); + printf("\tFCR:$%02x\t\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x MSR: $%02x Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", (int)FCR, (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); + printf("\n"); + }//*/ +#endif + int availCount = this->dataAvailable(); + // Pollings the modem status register [CTS/DSR] too quickly on some FTD232R or CH340 chips + // cause a stall due to throughput limit. Some X16 programs hammer this to detect connection. + // Given most Host OSes have HUGE buffers, let's assume we are always ready to send or recieve + // data if the Port was opened succesfully. + bool hasCTS = availCount < 14 ? true: false; //this->serialPort.isCTS(); + bool hasDSR = true; //this->serialPort.isDSR(); + switch(address & 0x07){ + case 0: + if ( (LCR & 0x80) /*DLAB=1*/) { + *value = DLSB; + retVal = 1; + } + else { + if( MCR & 0x10 ){ + *value=loopvalue; + retVal=loopvalue; + } else { + if( availCount > 0){ + retVal=this->read(value); + } + else { + retVal = -1; + *value = 0; + } + } + } + + break; + case 1: + if( LCR & 0x80 /*DLAB=1*/){ + *value = DMSB; + retVal = 1; + } + else{ + *value=IER; + retVal=1; + } + break; + case 2: + if (FCR & 0x01){ + IIR |= 0xC0; + } + else{ + IIR &= 0x3F; + } + + *value=IIR; + retVal=1; + break; + case 3: + *value=LCR; + retVal=1; + break; + case 4: + //Modem Control Register + *value=MCR; + retVal=1; + break; + case 5: + //Line Status Register + if(availCount > 0){ + LSR |= 0x01; //Data Ready + } + else { + LSR &= 0xFE; + } + *value=LSR; + retVal=1; + break; + case 6: + //Modem Status Register - Update on read + //if( (MCR & 0x01) && this->serialPort.isCTS() ) {//CTS + if( hasCTS ) { + MSR |= 0b00100000; + } + else { + MSR &= 0b11011111; + } + + //if( (MCR & 0x02) && this->serialPort.isDSR() ){//DSR + if( hasDSR ){ + MSR |= 0b00010000; + } + else { + MSR &= 0b11101111; + } + + *value=MSR; + retVal = MSR; + break; + case 7: + //Scratch Register + retVal = 1; + *value = scratchReg; + break; + default: + retVal=-2; + break; + } + + return retVal; +} + +void serialuartTL16C2550::reconfigureSerial(){ + + //This may get called a lot depending on the calling program and how it was written. + //Every change to LCR or MCR constitutes a reconfiguration of the hardware settings. + //if(!has_uart1) + //return; + + //Serial Port Configuration enumerators + SerialDataBits Databits; + SerialStopBits Stopbits; + SerialParity Parity; + + int wordLength=0; + + if(this->serialPort.isDeviceOpen()){ + // Close the serial device in reset or re-initilization + this->serialPort.closeDevice(); + } + + //Handle Databits + wordLength = (LCR&0x03); + switch( wordLength ){ + case 0: + Databits = SERIAL_DATABITS_5; + break; + case 1: + Databits = SERIAL_DATABITS_6; + break; + case 2: + Databits = SERIAL_DATABITS_7; + break; + case 3: + Databits = SERIAL_DATABITS_8; + break; + default: + Databits = SERIAL_DATABITS_8; + break; + } + + //Handle Stop Bits + if (LCR & 0x04){ + if(wordLength==5){ + Stopbits = SERIAL_STOPBITS_1_5; + } else { + Stopbits = SERIAL_STOPBITS_2; + } + } else { + Stopbits = SERIAL_STOPBITS_1; + } + + //Handle Parity + if( LCR & 0x08 ){ //Bit 3 Enabled + if( LCR & 0x20){ //Bit 5 Enabled + if( LCR & 0x10 ){ //Bit 4 Enabled + Parity = SERIAL_PARITY_SPACE; + } + else { //Bit 4 Disabled + Parity = SERIAL_PARITY_MARK; + } + } + else { //Bit 5 disabled + if( LCR & 0x10 ){ + Parity = SERIAL_PARITY_EVEN; + } else { + Parity = SERIAL_PARITY_ODD; + } + } + } + else { + Parity = SERIAL_PARITY_NONE; + } + + //Connect to real serial port + //int errorOpening = serialPort.openDevice(path.c_str(), baudCalculator(requestedDivisor)); + + int errorOpening = this->serialPort.openDevice(path.c_str(), baudCalculator(requestedDivisor), + Databits, + Parity, + Stopbits); + + //DTR is alreasy ready when port is open + this->serialPort.DTR(MCR & 0x01); + //RTS is controlled from AFE bitof MCR (always ready if flow control is enabled + this->serialPort.RTS( MCR & 0x20 ); + + // If connection fails, return the error code and display a success message + if (errorOpening!=1){ + std::cerr<(Parity)); + std::cerr<<"\tStopbits: "<(Stopbits)); + std::cerr<<"\tDivisor: "<path<(Parity))<(Stopbits))<serialPort.isDeviceOpen()){ + // Close the serial device in reset or initilization + this->serialPort.closeDevice(); + } + + + // Connection to serial port +#if defined (_WIN32) || defined( _WIN64) + //Windows can't open COM ports greater than COM9 without this. + path ="\\\\.\\\\"; +#else + path =""; +#endif + + path += (char*)port; + std::cout<<"Attempting: "<serialPort.openDevice(path.c_str(), 115200); + + // If connection fails, return the error code and display a success message + if (errorOpening!=1){ + std::cout<<(int)errorOpening<<": "; + switch(errorOpening){ + case -1: + std::cerr<<"device not found\n"; + break; + case -2: + std::cerr<<"error while opening the device\n"; + break; + case -3: + std::cerr<<"error while getting port parameters\n"; + break; + case -4: + std::cerr<<"Speed (Bauds) not recognized\n"; + break; + case -5: + std::cerr<<"error while writing port parameters\n"; + break; + case -6: + std::cerr<<"error while writing timeout parameters\n"; + break; + case -7: + std::cerr<<"Databits not recognized\n"; + break; + case -8: + std::cerr<<"Stopbits not recognized\n"; + break; + case -9: + std::cerr<<"Parity not recognized\n"; + break; + default: + std::cout<<": Error opening: "<serialPort.DTR(false); + this->serialPort.RTS(false); + + return 0; +} + +int serialuartTL16C2550::write(unsigned char* val){ + //Write one byte at a time. + //FIFO possible, but wouldn't match speed of hardware. + int retVal= this->serialPort.writeBytes(val,1); + return retVal; +} + +int serialuartTL16C2550::read(unsigned char* readVal){ + //Read one byte at a time... Only way to mimic hardware. + return this->serialPort.readBytes(readVal,1,1000,1000); +} + +int serialuartTL16C2550::dataAvailable(){ + //Bytes available from computer UART + return this->serialPort.available(); +} + +int serialuartTL16C2550::baudCalculator(uint16_t divisor){ + if(divisor > 0) + return ( (OSC/divisor) / 16 ); + else + return -1; +} + +std::string serialuartTL16C2550::descParity(int parity){ + + std::string results; + + switch(parity){ + case 0: + results="No Parity"; + break; + case 1: + results="Even Parity"; + break; + case 2: + results="Odd Parity"; + break; + case 3: + results="Mark Parity"; + break; + case 4: + results="Space Parity"; + break; + default: + results="Error"; + break; + } + return results; +} + +std::string serialuartTL16C2550::descStopbits(int stopbits){ + std::string results; + + switch(stopbits){ + case 0: + results="1 bit"; + break; + case 1: + results="1.5 bits"; + break; + case 2: + results="2 bits"; + break; + default: + results="Error"; + break; + } + return results; +} diff --git a/src/extern/serialuart/serialuartTL16C2550.hpp b/src/extern/serialuart/serialuartTL16C2550.hpp new file mode 100644 index 00000000..9b02a9d9 --- /dev/null +++ b/src/extern/serialuart/serialuartTL16C2550.hpp @@ -0,0 +1,64 @@ +/*! +\file serialuartTL16C2550.hpp +\brief Header file of the class serialuartTL16C2550. A serial UART for Commander X16 Emulator +\author Jason Hill +\version 0.1 +\date September 18th of 2025, by Jason Hill +\modified none + +This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating +some aspects of the TL16C2550 for use on personal computers. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This is a licence-free software, it can be used by anyone who try to build a better world. +*/ + +#pragma once + +#include +#include +#include +#include "serialib/serialib.h" + +extern unsigned char scratchReg; +extern bool has_uart1; +extern bool has_uart2; + +class serialuartTL16C2550{ + public: + + //serialuartTL16C2550(); + ~serialuartTL16C2550(); + int init(char*); + int addrwrite(unsigned char *value, int address); + int addrread(unsigned char* value, int address); + + private: + //UART Registers + unsigned char IIR, IER, + FCR, MCR, LCR, LSR, MSR, + scratchReg, + DLSB, DMSB, + loopvalue; + uint16_t requestedDivisor; + serialib serialPort; + + //Crystal Oscilator Speed on Texelec Serial/Wifi Card + static const int OSC = 14745600; + std::string path; + void reconfigureSerial(); + int write(unsigned char*); + int read( unsigned char*); + int dataAvailable(); + int baudCalculator(uint16_t); + + //For easy descriptions on errors + std::string descParity(int); + std::string descStopbits(int); + +}; diff --git a/src/extern/serialuart/serialuart_wrapper.cpp b/src/extern/serialuart/serialuart_wrapper.cpp new file mode 100644 index 00000000..e940db2a --- /dev/null +++ b/src/extern/serialuart/serialuart_wrapper.cpp @@ -0,0 +1,64 @@ +/*! +\file serialuart_wrapper.cpp +\brief Sourc file to wrap the class serialuartTL16C2550 for use in C. +\author Jason Hill +\version 0.1 +\date September 18th of 2025, by Jason Hill +\modified none + +This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating +some aspects of the TL16C2550 for use on personal computers. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This is a licence-free software, it can be used by anyone who try to build a better world. +*/ + + +#include "serialuart_wrapper.h" +#include "serialuartTL16C2550.hpp" + +serialuartTL16C2550Handle uartCreate() { + return static_cast(new serialuartTL16C2550()); +} + +int uart_init( serialuartTL16C2550Handle uart, char *value ) { + + serialuartTL16C2550* obj = static_cast(uart); + if (obj != NULL) { + return obj->init(value); + } + + return -1; +} + +int uart_addrwrite( serialuartTL16C2550Handle uart, unsigned char *value, int address ) { + + serialuartTL16C2550* obj = static_cast(uart); + if (obj !=NULL) { + return obj->addrwrite(value, address); + } + + return -1; +} + +int uart_addrread( serialuartTL16C2550Handle uart, unsigned char *value, int address ) { + + serialuartTL16C2550* obj = static_cast(uart); + if (obj!=NULL) { + return obj->addrread(value, address); + } + return -1; +} + +void uart_destroy(serialuartTL16C2550Handle uart) { + + serialuartTL16C2550* obj = static_cast(uart); + delete obj; + obj = NULL; + +} diff --git a/src/extern/serialuart/serialuart_wrapper.h b/src/extern/serialuart/serialuart_wrapper.h new file mode 100644 index 00000000..60ae2037 --- /dev/null +++ b/src/extern/serialuart/serialuart_wrapper.h @@ -0,0 +1,42 @@ +/*! +\file serialuart_wrapper.h +\brief Header file to wrap the class serialuartTL16C2550 for use in C. +\author Jason Hill +\version 0.1 +\date September 18th of 2025, by Jason Hill +\modified none + +This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating +some aspects of the TL16C2550 for use on personal computers. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +This is a licence-free software, it can be used by anyone who try to build a better world. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +// Pointer for the C++ Class Instance +typedef void* serialuartTL16C2550Handle; + +// Constructor-like function +serialuartTL16C2550Handle uartCreate(); + +// Method-calling function +int uart_init( serialuartTL16C2550Handle, char*); +int uart_addrwrite( serialuartTL16C2550Handle , unsigned char*, int); +int uart_addrread( serialuartTL16C2550Handle , unsigned char*, int); + + +// Destructor-like function +void uart_destroy( serialuartTL16C2550Handle ); + +#ifdef __cplusplus +} +#endif diff --git a/src/main.c b/src/main.c index efa94eb5..971ff094 100644 --- a/src/main.c +++ b/src/main.c @@ -44,6 +44,7 @@ #include "cartridge.h" #include "midi.h" #include "git_rev.h" +#include "extern/serialuart/serialuart_wrapper.h" #ifdef __EMSCRIPTEN__ #include @@ -120,6 +121,8 @@ char *gif_path = NULL; char *wav_path = NULL; uint8_t *fsroot_path = NULL; uint8_t *startin_path = NULL; +uint8_t *uart1_path = NULL; +uint8_t *uart2_path = NULL; uint8_t keymap = 0; // KERNAL's default int window_scale = 1; float screen_x_scale = 1.0; @@ -136,6 +139,15 @@ char *cartridge_path = NULL; bool has_midi_card = false; uint16_t midi_card_addr; +//Serial UART Setup +bool has_uart1 = false; +uint16_t uart1_addr = 0x9fe0; +serialuartTL16C2550Handle uart1 = NULL; + +bool has_uart2 = false; +uint16_t uart2_addr = 0x9fe8; +serialuartTL16C2550Handle uart2 = NULL; + bool using_hostfs = true; uint8_t MHZ = 8; @@ -340,6 +352,35 @@ machine_reset() mouse_state_init(); reset6502(regs.is65c816); midi_serial_init(); + + //User requested Serial UART - need to check + if(has_uart1){ + if(uart1 != NULL){ + uart_destroy(uart1); + } + uart1 = uartCreate(); + if(uart1 != NULL){ + if (uart_init( uart1, (char*)uart1_path ) ){ + has_uart1=false; + } + } else { + has_uart1=false; + } + } + if(has_uart2){ + if(uart2 != NULL){ + uart_destroy(uart2); + } + + uart2 = uartCreate(); + if(uart2 != NULL){ + if (uart_init( uart2, (char*)uart2_path ) ){ + has_uart2=false; + } + } else { + has_uart2=false; + } + } } void @@ -546,6 +587,15 @@ usage() printf("\tConnect the system MIDI input devices to the input of the first UART\n"); printf("\tof the emulated MIDI card. The -midicard option is required for this\n"); printf("\toption to have any effect.\n"); + printf("-uart1 []\n\texample: [/dev/ttyUSB0] (Posix) COM1 (Windows)\n"); + printf("\tConnect the system COM port devices to the the first UART\n"); + printf("\tThis option is experimental.\n"); + printf("-uart2 []\n\texample: /dev/ttyACM0 (Posix) COM2 (Windows)\n"); + printf("\tConnect the system COM port devices to the the second UART\n"); + printf("\tThis option is experimental.\n"); + printf("-uartaddr [
]\n"); + printf("\tIf not specified, defaults to '$9FE0'\n\tUART2 is alwasy UART1 address + 8.\n"); + printf("\tThis option is experimental.\n"); #ifdef TRACE printf("-trace [
]\n"); printf("\tPrint instruction trace. Optionally, a trigger address\n"); @@ -1145,6 +1195,46 @@ main(int argc, char **argv) argc--; argv++; warn_rockwell = false; + } else if (!strcmp(argv[0], "-uart1")) { + argc--; + argv++; + if (!argc || argv[0][0] == '-') { + usage(); + } + uart1_path = (uint8_t *)argv[0]; + argc--; + argv++; + has_uart1=true; + + } else if (!strcmp(argv[0], "-uartaddr")) { + argc--; + argv++; + if (!argc || argv[0][0] == '-') { + usage(); + } + + if (argc && argv[0][0] != '-') { + uart1_addr = 0x9f00 | ((uint16_t)strtol(argv[0], NULL, 16) & 0xff); + uart1_addr &= 0xfff0; + argc--; + argv++; + } else { + uart1_addr = 0x9fe0; + } + + uart2_addr = uart1_addr+8; + + } else if (!strcmp(argv[0], "-uart2")) { + argc--; + argv++; + if (!argc || argv[0][0] == '-') { + usage(); + } + uart2_path = (uint8_t *)argv[0]; + argc--; + argv++; + has_uart2=true; + } else { usage(); } @@ -1198,6 +1288,13 @@ main(int argc, char **argv) fprintf(stderr, "Warning: -sf2 and -midicard must be specified together in order to enable the MIDI synth.\n"); has_midi_card = false; } + + if (has_uart2 || has_uart1) { + if (uart1_addr < 0x9f60 ) { + fprintf(stderr, "Warning: Serial UART card address must be in the range of 9F60-9FF0, Unabled to connect\n"); + has_uart1 = has_uart2 = false; + } + } if (cartridge_path) { if (!cartridge_load(cartridge_path, !zeroram)) { diff --git a/src/memory.c b/src/memory.c index 14f51900..28e79d49 100644 --- a/src/memory.c +++ b/src/memory.c @@ -20,6 +20,14 @@ #include "iso_8859_15.h" #include "midi.h" +#include "extern/serialuart/serialuart_wrapper.h" +extern bool has_uart1; +extern bool has_uart2; +extern uint16_t uart1_addr; +extern uint16_t uart2_addr; +extern serialuartTL16C2550Handle uart1; +extern serialuartTL16C2550Handle uart2; + uint8_t ram_bank; uint8_t rom_bank; @@ -242,7 +250,20 @@ real_read6502(uint16_t address, uint8_t bank, bool debugOn, int16_t x16Bank) } else if (has_midi_card && (address & 0xfff0) == midi_card_addr) { // midi card return midi_serial_read(address & 0xf, debugOn); - } else { + } + //Serial UART1 (serial wifi) + else if ( has_uart1 && (address >= uart1_addr && address <= (uart1_addr+7) ) ){ + uint8_t retVal=0; + uart_addrread(uart1, &retVal, address & 0x07 ); + return retVal; + }// END of Serial UART1*/ + //Serial UART2 (serial wifi) + else if ( has_uart2 && (address >= uart2_addr && address <= (uart2_addr+7)) ){ + uint8_t retVal=0; + uart_addrread(uart2, &retVal, address & 0x07 ); + return retVal; + }// END of Serial UART2*/ + else { // future expansion return 0x9f; // open bus read } @@ -332,7 +353,15 @@ write6502(uint16_t address, uint8_t bank, uint8_t value) emu_write(address & 0xf, value); } else if (has_midi_card && (address & 0xfff0) == midi_card_addr) { midi_serial_write(address & 0xf, value); - } else { + } + else if (has_uart1 && (address >= uart1_addr && address <= (uart1_addr+7) ) ) { + uart_addrwrite(uart1, &value, address & 0x07 ); + } + else if (has_uart2 && (address >= uart2_addr && address <= (uart2_addr+7) ) ) { + uart_addrwrite(uart2, &value, address & 0x07 ); + } + //END of UART2 + else { // future expansion } } else if (address < 0xc000) { // banked RAM From ad9bcf93c37525724bedf30461773a4d7f6e3094 Mon Sep 17 00:00:00 2001 From: "U-MORPHINE-TR4\\morphinejh" <78104266+morphinejh@users.noreply.github.com> Date: Sun, 15 Feb 2026 15:15:25 -0500 Subject: [PATCH 2/7] add Release to make file --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index bdffb3c1..c1b3e6d2 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,7 @@ BUILD_DIR = build all: @cmake -E echo "Building with CMake" cmake -E make_directory $(BUILD_DIR) - cmake -S . -B $(BUILD_DIR) + cmake -S . -B $(BUILD_DIR) -DCMAKE_BUILD_TYPE=Release cmake --build $(BUILD_DIR) $(ARGS) @cmake -E echo "x16emu and makecart executables can be found in ./$(BUILD_DIR)" From 4878410a24c2b785dcc1ce0702de6ef50a849c9d Mon Sep 17 00:00:00 2001 From: "U-MORPHINE-TR4\\morphinejh" <78104266+morphinejh@users.noreply.github.com> Date: Tue, 3 Mar 2026 23:27:33 -0500 Subject: [PATCH 3/7] addded IRQ, FIFO, and UART threading --- src/extern/serialuart/serialuartTL16C2550.cpp | 1464 +++++++++++------ src/extern/serialuart/serialuartTL16C2550.hpp | 238 ++- src/extern/serialuart/serialuart_wrapper.cpp | 10 + src/extern/serialuart/serialuart_wrapper.h | 1 + src/main.c | 4 +- 5 files changed, 1180 insertions(+), 537 deletions(-) diff --git a/src/extern/serialuart/serialuartTL16C2550.cpp b/src/extern/serialuart/serialuartTL16C2550.cpp index 84a7df30..e106f5ab 100644 --- a/src/extern/serialuart/serialuartTL16C2550.cpp +++ b/src/extern/serialuart/serialuartTL16C2550.cpp @@ -2,23 +2,34 @@ \file serialuartTL16C2550.cpp \brief Source file of the class serialuartTL16C2550. A serial UART for Commander X16 Emulator \author Jason Hill -\version 0.1 +\version 0.3 \date September 18th of 2025, by Jason Hill -\modified none +\modified March 2026 – Added 16-byte RX/TX FIFOs, threading, and full TL16C2550 interrupt system This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating some aspects of the TL16C2550 for use on personal computers. -Description: This file will simulate many functions of the Texas Instruments TL16C2550 UART. Some functions -have been omtited due to arhcitectrual differences between hardware and OS control. Some FIFO functionas -are 'faked' for ease of use. - -This file will emulate one serial port by attaching it to a physical port on the computer. Adjusting port -configurations as necessary based on register settings as they are programmed.It is not a complete -implementation, but will run most programs. - -On hardware, UART1 is connected to an ESP32 on the X16 Serial/Wifi Card. An ESP8266 or ESP32 will -need to be attached to a COM port, with proper firmware, to get the same functionality. +Description: This file simulates the Texas Instruments TL16C2550 UART as faithfully as an OS-level +serial port allows. The implementation adds: + + - True 16-byte RX and TX FIFOs (std::deque, capacity-limited to UART_HW_FIFO_SIZE). + - Per-character error flags (OE/PE/FE/BI) stored with each RX FIFO entry and exposed + through LSR only when the flagged character is at the head of the FIFO. + - Full TL16C2550 interrupt priority chain: + 1. Receiver Line Status (OE/PE/FE/BI) – highest priority + 2. Received Data Available / FIFO trigger + 3. Character Timeout (FIFO idle 4 char-times) + 4. Transmitter Holding Register Empty + 5. Modem Status change – lowest priority + - IIR correctly encodes the highest-priority pending source; bits 7:6 reflect + FIFO-enabled state. + - IRQ output gated by MCR OUT2 (bit 3), exposed via irqPending(). + - MSR delta bits (DCTS/DDSR) set on modem signal transitions, cleared on MSR read. + - Thread-safe FIFO access: threadCycleUpdate() pushes RX / drains TX from a background + thread; addrread() / addrwrite() called/run on the emulator thread. + +On hardware, UART1 is connected to an ESP32 on the X16 Serial/Wifi Card. An ESP8266 or +ESP32 with appropriate firmware must be attached to a host COM port for the same function. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR @@ -30,522 +41,981 @@ This is a licence-free software, it can be used by anyone who try to build a bet */ //#define DEBUGUART -//#define VERBOSE +//#define VERBOSEUART #include "serialuartTL16C2550.hpp" +// =========================================================================== +// Constructor / Destructor +// =========================================================================== + +serialuartTL16C2550::serialuartTL16C2550(): + IIR(IIR_NO_INT), IER(0), FCR(0), MCR(0), LCR(0), LSR(LSR_THRE | LSR_TEMT), + MSR(0), scratchReg(0), DLSB(0x60), DMSB(0), requestedDivisor(0x60), + loopvalue(0), + irqRxLineStatus(false), irqRxDataReady(false), irqCharTimeout(false), + irqThrEmpty(false), irqModemStatus(false), + rxTimeoutTicks(0), rxTimeoutThreshold(40), ticksPerChar(4), + prevCTS(false), prevDSR(false), + quit(false), hasCTS(false), hasDSR(false), availCount(0), + checksperbaud(5), //At least (2)twice as fast as baud rate for no delays, 5 is plenty fast + fifo_level(1), + baud_delay(std::chrono::nanoseconds((1000000000 / 9600) / 10)) +{ + //Nothing to see here - see initilization list. +} + serialuartTL16C2550::~serialuartTL16C2550(){ - this->serialPort.closeDevice(); + quit = true; + if (t.joinable()) { + t.join(); + } + this->serialPort.closeDevice(); +} + +// =========================================================================== +// irqPending +// +// The TL16C2550 drives its IRQ pin low when any enabled interrupt source is +// active AND MCR bit 3 (OUT2) is set. +// =========================================================================== +bool serialuartTL16C2550::irqPending() const { + if (!(MCR & MCR_OUT2)) + return false; // IRQ output tri-stated when OUT2 = 0 + return (IIR & 0x01) == 0; // IIR bit 0 = 0 means interrupt pending +} + +// =========================================================================== +// updateIIR +// +// Rebuilds IIR from the five interrupt-pending flags and the IER mask. +// Must be called after any change to the flags, IER, FCR, or RX FIFO depth. +// +// Priority (TL16C2550 datasheet Table 3): +// 1 = RLS – Receiver Line Status +// 2 = RDA – Received Data Available / FIFO trigger +// 3 = CTI – Character Timeout Indication (FIFO mode only) +// 4 = THRE – Transmitter Holding Register Empty +// 5 = MSR – Modem Status Register +// =========================================================================== +void serialuartTL16C2550::updateIIR(){ + unsigned char newIIR = IIR_NO_INT; // default: no interrupt pending + + // Priority 1 – Receiver Line Status + if ((IER & IER_ELSI) && irqRxLineStatus) { + newIIR = IIR_RX_LINE_STAT; + } + // Priority 2 – Received Data Available (FIFO at or above trigger level) + else if ((IER & IER_ERBFI) && irqRxDataReady) { + newIIR = IIR_RX_DATA; + } + // Priority 3 – Character Timeout (FIFO mode, stale data) + else if ((IER & IER_ERBFI) && irqCharTimeout) { + newIIR = IIR_CHAR_TIMEOUT; + } + // Priority 4 – Transmitter Holding Register Empty + else if ((IER & IER_ETBEI) && irqThrEmpty) { + newIIR = IIR_THR_EMPTY; + } + // Priority 5 – Modem Status + else if ((IER & IER_EDSSI) && irqModemStatus) { + newIIR = IIR_MODEM_STATUS; + } + + // Bits 7:6 indicate FIFO enabled (set to 11 when FCR bit 0 = 1) + if (FCR & FCR_FIFO_EN) { + newIIR |= IIR_FIFO_BITS; + } else { + newIIR &= ~IIR_FIFO_BITS; + } + + IIR = newIIR; +} + +// =========================================================================== +// computeLSR +// +// Builds the live LSR value from the current FIFO state plus the 'sticky' +// error bits that are held in the LSR member until LSR is read. +// +// The TL16C2550 reports per-character error flags (PE/FE/BI) only for the +// character currently at the head of the RX FIFO. OE is set as soon as an +// overrun occurs and is also 'sticky' until LSR is read. +// =========================================================================== +unsigned char serialuartTL16C2550::computeLSR() const { + // Start from the 'sticky' error bits (OE/PE/FE/BI survive until LSR read) + unsigned char lsr = LSR & (LSR_OE | LSR_PE | LSR_FE | LSR_BI); + + // DR – data ready: at least one byte in RX FIFO + if (!rxFifo.empty()) { + lsr |= LSR_DR; + // Per-character errors from the head entry + lsr |= rxFifo.front().err & (LSR_PE | LSR_FE | LSR_BI); + } + + // THRE – TX FIFO (or holding register) empty + if (txFifo.empty()) { + lsr |= LSR_THRE; + } + + // TEMT – transmitter completely empty (FIFO + shift register) + // We don't model the shift register separately; treat TEMT = THRE. + if (txFifo.empty()) { + lsr |= LSR_TEMT; + } + + // RXFE (bit 7) – FIFO mode only: at least one error in any FIFO entry + if (FCR & FCR_FIFO_EN) { + for (const auto &entry : rxFifo) { + if (entry.err) { + lsr |= LSR_RXFE; + break; + } + } + } + + return lsr; +} + +// =========================================================================== +// rxFIFOPush +// +// background threadCycleUpdate() +// Pushes one received byte into the RX FIFO. If the FIFO is full an overrun +// is declared: LSR OE is set, the line-status interrupt is armed, and the new +// byte is discarded (matching TL16C2550 behaviour). +// =========================================================================== +void serialuartTL16C2550::rxFifoPush(unsigned char data, unsigned char errFlags){ + std::lock_guard lock(rxMutex); + + int cap = (FCR & FCR_FIFO_EN) ? UART_HW_FIFO_SIZE : 1; + + if ((int)rxFifo.size() >= cap) { + // Overrun: set OE in 'sticky' LSR bits + LSR |= LSR_OE; + irqRxLineStatus = true; + updateIIR(); + return; // discard the new byte per TL16C2550 datasheet + } + + rxFifo.push_back({data, errFlags}); + + // Any per-character error arms the line-status interrupt + if (errFlags & (LSR_PE | LSR_FE | LSR_BI)) { + irqRxLineStatus = true; + } + + // Check RX trigger level for DataAvailable interrupt + int trigger = (int)fifo_level; // 1, 4, 8, or 14 + if ((int)rxFifo.size() >= trigger) { + irqRxDataReady = true; + irqCharTimeout = false; // DataAvailable takes priority; reset timeout + rxTimeoutTicks = 0; + } + + // Any new byte resets the character-timeout counter + rxTimeoutTicks = 0; + irqCharTimeout = false; + + updateIIR(); +} + +// =========================================================================== +// rxFIFOPop +// +// background threadCycleUpdate() +// +// Removes and returns the head entry. Updates DR, RXFE, DataAvailable, and timeout +// interrupt state after the pop. +// =========================================================================== +bool serialuartTL16C2550::rxFifoPop(UartRxEntry &out){ + std::lock_guard lock(rxMutex); + + if (rxFifo.empty()) + return false; + + out = rxFifo.front(); + rxFifo.pop_front(); + + // If FIFO fell below trigger level, clear DataAvailable interrupt + if ((int)rxFifo.size() < (int)fifo_level) { + irqRxDataReady = false; + } + + // If FIFO is now empty, clear timeout interrupt and reset counter + if (rxFifo.empty()) { + irqCharTimeout = false; + rxTimeoutTicks = 0; + } + + // Re-evaluate line-status interrupt: + // irqRxLineStatus stays set until LSR is read 'sticky', but if the + // head-character error was the only trigger and the head just changed, + // the new head's errors will be reported on the next LSR read. + + updateIIR(); + return true; } -int serialuartTL16C2550::addrwrite(unsigned char* value, int address){ - int retVal = 0; +// =========================================================================== +// rxFifoReset (FCR bit 1) +// =========================================================================== +void serialuartTL16C2550::rxFifoReset(){ + std::lock_guard lock(rxMutex); + rxFifo.clear(); + irqRxDataReady = false; + irqCharTimeout = false; + rxTimeoutTicks = 0; + // Clear DR and RXFE in the 'sticky' LSR; leave OE/PE/FE/BI until LSR read + LSR &= ~LSR_DR; + updateIIR(); +} + +// =========================================================================== +// txFifoReset (FCR bit 2) +// =========================================================================== +void serialuartTL16C2550::txFifoReset(){ + { + std::lock_guard lock(txMutex); + txFifo.clear(); + } + // Empty TX → arm THRE interrupt + irqThrEmpty = true; + LSR |= (LSR_THRE | LSR_TEMT); + updateIIR(); +} + +// =========================================================================== +// updateMSR +// +// Called by threadCycleUpdate() when CTS or DSR changes. Sets the corresponding +// delta bits in MSR (DCTS = bit 0, DDSR = bit 1) and arms the modem-status +// interrupt. The delta bits are 'sticky' until MSR is read. +// =========================================================================== +void serialuartTL16C2550::updateMSR(bool newCTS, bool newDSR){ + bool changed = false; + + if (newCTS != prevCTS) { + MSR |= 0x01; // DCTS + changed = true; + prevCTS = newCTS; + } + if (newDSR != prevDSR) { + MSR |= 0x02; // DDSR + changed = true; + prevDSR = newDSR; + } + + // Update the current-state bits (bits 5:4 = DSR/CTS on TL16C2550) + if (newCTS) MSR |= 0x20; else MSR &= ~0x20; + if (newDSR) MSR |= 0x10; else MSR &= ~0x10; + + if (changed) { + irqModemStatus = true; + updateIIR(); + } +} + +// =========================================================================== +// threadCycleUpdate (background thread) +// +// Runs at roughly x2 the baud rate. Tasks: +// 1. Drain the TX FIFO to the host serial port (respecting AFE/CTS). +// 2. Push available bytes from the host port into the RX FIFO. +// 3. Advance the character-timeout counter and fire the timeout interrupt +// if the FIFO has been idle for RX_TIMEOUT_CHARS character-times. +// 4. Manage RTS for automatic flow control (MCR AFE bit). +// 5. Detect CTS/DSR transitions and update MSR/interrupt. +// =========================================================================== +void serialuartTL16C2550::threadCycleUpdate(){ + while (!this->quit) { + + // ---------------------------------------------------------------- + // 1. Refresh modem signal states and update MSR / IRQ + // ---------------------------------------------------------------- + bool newCTS = this->serialPort.isCTS(); + bool newDSR = this->serialPort.isDSR(); + hasCTS = newCTS; + hasDSR = newDSR; + updateMSR(newCTS, newDSR); + + // ---------------------------------------------------------------- + // 2. TX: drain TX FIFO → host serial port + // In AFE mode, only send when CTS is asserted. + // ---------------------------------------------------------------- + { + std::lock_guard lock(txMutex); + if (!txFifo.empty()) { + bool allowSend = true; + if (MCR & MCR_AFE) { + allowSend = hasCTS; + } + if (allowSend) { + unsigned char txByte = txFifo.front(); + txFifo.pop_front(); + + // Write to physical port + this->serialPort.writeBytes(&txByte, 1); + + // If FIFO just became empty, arm THRE interrupt + if (txFifo.empty()) { + irqThrEmpty = true; + LSR |= (LSR_THRE | LSR_TEMT); + updateIIR(); + } + } + } + } + + // ---------------------------------------------------------------- + // 3. RX: pump host port → RX FIFO + // Read up to (FIFO capacity – current occupancy) bytes so we + // never overflow the FIFO in one tick. + // ---------------------------------------------------------------- + availCount = this->dataAvailable(); + if (availCount > 0) { + int cap = (FCR & FCR_FIFO_EN) ? UART_HW_FIFO_SIZE : 1; + int occupied; + { + std::lock_guard lock(rxMutex); + occupied = (int)rxFifo.size(); + } + int canReceive = cap - occupied; + + for (int i = 0; i < canReceive && i < availCount; ++i) { + unsigned char rxByte = 0; + int result = this->serialPort.readBytes(&rxByte, 1, 0, 0); + if (result <= 0) + break; + // Push with no error flags; physical parity/framing errors + // are surfaced by the host OS and could be added here if the + // OS API exposes them (platform-specific extension point). + rxFifoPush(rxByte, 0); + } + } + + // ---------------------------------------------------------------- + // 4. Character Timeout + // If the RX FIFO is not empty and no new byte has arrived for + // RX_TIMEOUT_CHARS character-times, assert the timeout interrupt. + // "One character-time" ≈ ticksPerChar background ticks. + // ---------------------------------------------------------------- + { + std::lock_guard lock(rxMutex); + if (!rxFifo.empty() && !irqRxDataReady) { + rxTimeoutTicks++; + if (rxTimeoutTicks >= rxTimeoutThreshold && !irqCharTimeout) { + irqCharTimeout = true; + updateIIR(); + } + } + // If FIFO drained to empty between ticks, reset the counter. + if (rxFifo.empty()) { + rxTimeoutTicks = 0; + irqCharTimeout = false; + } + } + + // ---------------------------------------------------------------- + // 5. AFE automatic RTS control + // Drop RTS when the RX FIFO reaches the trigger level so the + // sender pauses; raise it again once the FIFO drains below trigger. + // ---------------------------------------------------------------- + if (MCR & MCR_AFE) { + std::lock_guard lock(rxMutex); + if ((int)rxFifo.size() >= (int)fifo_level) { + if (serialPort.isRTS()) { + this->serialPort.clearRTS(); + } + } else { + if (!serialPort.isRTS()) { + this->serialPort.setRTS(); + } + } + } + + std::this_thread::sleep_for(std::chrono::nanoseconds(500)); + if (this->quit) + break; + } +} -#ifdef DEBUGUART //Debug - if(address != 0){ - printf("WriteAddress: $%02x\tValue: $%02x", address,(int)(*value)); - printf("\t-----\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x MSR: $%02x Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); - printf(" Divisor: %d\tBaud: %d\n", requestedDivisor, baudCalculator(requestedDivisor) ); - }//*/ +// =========================================================================== +// addrwrite - address write +// +// Called by the emulator when 6502 code writes to a UART register address. +// address bits 2:0 select the register; bit 3 (or higher) selects the channel +// on multi-channel devices – this class models one channel so we mask to 0x07. +// =========================================================================== +int serialuartTL16C2550::addrwrite(unsigned char *value, int address){ + int retVal = 0; + +#ifdef DEBUGUART + if (address != 0) { + printf("WriteAddress: $%02x\tValue: $%02x", address, (int)(*value)); + printf("\t-----\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x MSR: $%02x" + " Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", + (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, + (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); + printf(" Divisor: %d\tBaud: %d\n", + requestedDivisor, baudCalculator(requestedDivisor)); + } #endif - bool LCRconfigDirty=false; //This triggers a closing and opening of the serial port. - - switch(address & 0x07){ - case 0: //Transmitter Holding Register - if( LCR & 0x80 /*DLAB=1*/){ - DLSB = *value; - retVal = DLSB; - } - else{ - if( MCR & 0x10 /*Loop back mode*/ ){ - loopvalue=*value; - retVal=1; - } else { - retVal=this->write(value); - } - } - - break; - - case 1: //Interrupt Enable Register - if( LCR & 0x80 /*DLAB=1*/){ - DMSB = *value; - retVal = DMSB; - - } else { - IER = (0x0F & (*value) ); - } - break; - - case 2: //FIFO Control Register - FCR = 0xCF & (*value); - retVal = FCR; - break; - - case 3: //Line Control Register - //Handle SerialPort reconfigurations - if(LCR & 0x80){ //We are in Divisor Latch Mode currently - if( ( (*value) & 0x80 ) == 0 ){ - //We are exiting Divisor Latch Mode; update baud rate - requestedDivisor = DLSB; - requestedDivisor |= ((uint16_t)(DMSB<<8)); - LCRconfigDirty=true; - } - } - - if ( (LCR&0x7F) != ( (*value)&0x7f ) ){ - LCRconfigDirty=true; - } - - LCR=*value; - - if(LCRconfigDirty){ - reconfigureSerial(); - } - retVal = LCR; - break; - - case 4: //Modem Control Register - if(*value != MCR){ - MCR =*value; - reconfigureSerial(); - } - retVal=MCR; - break; - - case 5: //Line Status Register - //LSR is read only - retVal=-5; - break; - - case 6: //Modem Status Register - //MSR is read only - retVal=-6; - break; - - case 7: //Scratch Register - scratchReg = *value; - retVal=0; - break; - - default: - break; - } - return retVal; + + bool LCRconfigDirty = false; + + switch (address & 0x07) { + + // ------------------------------------------------------------------- + // Offset 0 – Transmit Holding Register (DLAB=0) / DLL (DLAB=1) + // ------------------------------------------------------------------- + case 0: + if (LCR & 0x80 /* DLAB = 1 */) { + DLSB = *value; + retVal = DLSB; + } else { + if (MCR & MCR_LOOPBACK) { + // Loopback: route the byte directly into the RX FIFO. + loopvalue = *value; + rxFifoPush(loopvalue, 0); + retVal = 1; + } else { + // Push into TX FIFO (respect capacity) + { + std::lock_guard lock(txMutex); + int cap = (FCR & FCR_FIFO_EN) ? UART_HW_FIFO_SIZE : 1; + if ((int)txFifo.size() < cap) { + txFifo.push_back(*value); + retVal = 1; + } else { + retVal = 0; // TX FIFO full – byte dropped (caller should check THRE) + } + } + // Writing to THR clears the THRE interrupt + irqThrEmpty = false; + LSR &= ~(LSR_THRE | LSR_TEMT); + updateIIR(); + } + } + break; + + // ------------------------------------------------------------------- + // Offset 1 – Interrupt Enable Register (DLAB=0) / DLM (DLAB=1) + // ------------------------------------------------------------------- + case 1: + if (LCR & 0x80 /* DLAB = 1 */) { + DMSB = *value; + retVal = DMSB; + } else { + IER = 0x0F & (*value); + // If ETBEI was just enabled and TX FIFO is already empty, + // immediately arm the THRE interrupt. + if ((IER & IER_ETBEI) && txFifo.empty()) { + irqThrEmpty = true; + } + updateIIR(); + retVal = IER; + } + break; + + // ------------------------------------------------------------------- + // Offset 2 – FIFO Control Register (write-only) + // + // FCR is write-only on real hardware; the shadow copy is kept in FCR. + // Bit 0: FIFO enable. Bits 1/2: RX/TX FIFO reset. Bits 7:6: RX trigger. + // ------------------------------------------------------------------- + case 2: { + unsigned char oldFCR = FCR; + FCR = 0xCF & (*value); // bits 4:3 are not used on TL16C2550 + + // Disabling FIFOs also clears them (datasheet §9.3) + if ((oldFCR & FCR_FIFO_EN) && !(FCR & FCR_FIFO_EN)) { + rxFifoReset(); + txFifoReset(); + } + + // Explicit reset commands (can be issued even while enabling) + if (FCR & FCR_RX_RESET) { + rxFifoReset(); + FCR &= ~FCR_RX_RESET; // self-clearing + } + if (FCR & FCR_TX_RESET) { + txFifoReset(); + FCR &= ~FCR_TX_RESET; // self-clearing + } + + // Decode RX trigger level from bits 7:6 + switch (FCR & FCR_RX_TRIG) { + case 0x00: fifo_level = 1; break; + case 0x40: fifo_level = 4; break; + case 0x80: fifo_level = 8; break; + case 0xC0: fifo_level = 14; break; + default: fifo_level = 1; break; + } + + // Compute character-timeout threshold in threadCycleUpdate ticks. + // One character-time at checksperbaud ticks per baud event ≈ + // checksperbaud ticks. Four character-times: + ticksPerChar = checksperbaud; + rxTimeoutThreshold = RX_TIMEOUT_CHARS * ticksPerChar; + + updateIIR(); + retVal = FCR; + break; + } + + // ------------------------------------------------------------------- + // Offset 3 – Line Control Register + // ------------------------------------------------------------------- + case 3: + // Detect exit from Divisor Latch Access mode → baud rate change + if (LCR & 0x80) { // currently DLAB=1 + if (!((*value) & 0x80)) { // transitioning to DLAB=0 + requestedDivisor = DLSB; + requestedDivisor |= ((uint16_t)(DMSB) << 8); + LCRconfigDirty = true; + } + } + // Detect change in word/parity/stop bits + if ((LCR & 0x7F) != ((*value) & 0x7F)) { + LCRconfigDirty = true; + } + + LCR = *value; + + if (LCRconfigDirty && !(LCR & 0x80)) { + reconfigureSerial(); + } + retVal = LCR; + break; + + // ------------------------------------------------------------------- + // Offset 4 – Modem Control Register + // ------------------------------------------------------------------- + case 4: + if (*value != MCR) { + MCR = *value; + + // DTR pin + if (MCR & MCR_DTR) { + if (!serialPort.isDTR()) this->serialPort.DTR(true); + } else { + if ( serialPort.isDTR()) this->serialPort.DTR(false); + } + + // RTS pin (only when not in AFE mode; AFE manages it automatically) + if (!(MCR & MCR_AFE)) { + if (MCR & MCR_RTS) { + if (!serialPort.isRTS()) this->serialPort.RTS(true); + } else { + if ( serialPort.isRTS()) this->serialPort.RTS(false); + } + } + + // Loopback mode: when entering loopback the MSR input bits + // are driven by the MCR output bits (OUT2→DCD, OUT1→RI, + // RTS→CTS, DTR→DSR) – see TL16C2550 datasheet §7.6 + if (MCR & MCR_LOOPBACK) { + bool lbCTS = (MCR & MCR_RTS) != 0; + bool lbDSR = (MCR & MCR_DTR) != 0; + updateMSR(lbCTS, lbDSR); + } + + // If ETBEI is enabled and TX is now empty, arm THRE + if ((IER & IER_ETBEI) && txFifo.empty()) { + irqThrEmpty = true; + updateIIR(); + } + } + retVal = MCR; + break; + + // ------------------------------------------------------------------- + // Offset 5 – Line Status Register (read-only) + // ------------------------------------------------------------------- + case 5: + retVal = -5; // writes ignored + break; + + // ------------------------------------------------------------------- + // Offset 6 – Modem Status Register (read-only) + // ------------------------------------------------------------------- + case 6: + retVal = -6; // writes ignored + break; + + // ------------------------------------------------------------------- + // Offset 7 – Scratch Register + // ------------------------------------------------------------------- + case 7: + scratchReg = *value; + retVal = 0; + break; + + default: + break; + } + return retVal; } -int serialuartTL16C2550::addrread(unsigned char* value, int address){ - int retVal = -1; - -#ifdef DEBUGUART //Debug - if(address != 0){ - printf("ReadAddress: $%02x", address); - printf("\tFCR:$%02x\t\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x MSR: $%02x Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", (int)FCR, (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); - printf("\n"); - }//*/ +// =========================================================================== +// addrread - Address Read +// +// Called by the emulator when 6502 code reads from a UART register address. +// =========================================================================== +int serialuartTL16C2550::addrread(unsigned char *value, int address){ + int retVal = -1; + +#ifdef DEBUGUART + if (address != 0) { + printf("ReadAddress: $%02x", address); + printf("\tFCR:$%02x\t\tIER: $%02x IIR: $%02x MCR: $%02x LSR: $%02x" + " MSR: $%02x Scratch: $%02x LCR: $%02x DLSB: $%02x DMSB: $%02x", + (int)FCR, (int)IER, (int)IIR, (int)MCR, (int)LSR, (int)MSR, + (int)scratchReg, (int)LCR, (int)DLSB, (int)DMSB); + printf("\n"); + } #endif - int availCount = this->dataAvailable(); - // Pollings the modem status register [CTS/DSR] too quickly on some FTD232R or CH340 chips - // cause a stall due to throughput limit. Some X16 programs hammer this to detect connection. - // Given most Host OSes have HUGE buffers, let's assume we are always ready to send or recieve - // data if the Port was opened succesfully. - bool hasCTS = availCount < 14 ? true: false; //this->serialPort.isCTS(); - bool hasDSR = true; //this->serialPort.isDSR(); - switch(address & 0x07){ - case 0: - if ( (LCR & 0x80) /*DLAB=1*/) { - *value = DLSB; - retVal = 1; - } - else { - if( MCR & 0x10 ){ - *value=loopvalue; - retVal=loopvalue; - } else { - if( availCount > 0){ - retVal=this->read(value); - } - else { - retVal = -1; - *value = 0; - } - } - } - - break; - case 1: - if( LCR & 0x80 /*DLAB=1*/){ - *value = DMSB; - retVal = 1; - } - else{ - *value=IER; - retVal=1; - } - break; - case 2: - if (FCR & 0x01){ - IIR |= 0xC0; - } - else{ - IIR &= 0x3F; - } - - *value=IIR; - retVal=1; - break; - case 3: - *value=LCR; - retVal=1; - break; - case 4: - //Modem Control Register - *value=MCR; - retVal=1; - break; - case 5: - //Line Status Register - if(availCount > 0){ - LSR |= 0x01; //Data Ready - } - else { - LSR &= 0xFE; - } - *value=LSR; - retVal=1; - break; - case 6: - //Modem Status Register - Update on read - //if( (MCR & 0x01) && this->serialPort.isCTS() ) {//CTS - if( hasCTS ) { - MSR |= 0b00100000; - } - else { - MSR &= 0b11011111; - } - - //if( (MCR & 0x02) && this->serialPort.isDSR() ){//DSR - if( hasDSR ){ - MSR |= 0b00010000; - } - else { - MSR &= 0b11101111; - } - - *value=MSR; - retVal = MSR; - break; - case 7: - //Scratch Register - retVal = 1; - *value = scratchReg; - break; - default: - retVal=-2; - break; - } - - return retVal; + + switch (address & 0x07) { + + // ------------------------------------------------------------------- + // Offset 0 – Receive Buffer Register (DLAB=0) / DLL (DLAB=1) + // ------------------------------------------------------------------- + case 0: + if (LCR & 0x80 /* DLAB = 1 */) { + *value = DLSB; + retVal = 1; + } else { + if (MCR & MCR_LOOPBACK) { + // In loopback the RX FIFO is used, so fall through to normal pop. + // (The loopvalue byte was already pushed into the FIFO on write.) + } + UartRxEntry entry{}; + if (rxFifoPop(entry)) { + *value = entry.data; + retVal = entry.data; + // Reading RBR clears the character-timeout interrupt + irqCharTimeout = false; + updateIIR(); + } else { + *value = 0; + retVal = -1; + } + } + break; + + // ------------------------------------------------------------------- + // Offset 1 – Interrupt Enable Register (DLAB=0) / DLM (DLAB=1) + // ------------------------------------------------------------------- + case 1: + if (LCR & 0x80 /* DLAB = 1 */) { + *value = DMSB; + retVal = 1; + } else { + *value = IER; + retVal = 1; + } + break; + + // ------------------------------------------------------------------- + // Offset 2 – Interrupt Identification Register (read-only) + // + // Reading IIR when THRE is the reported source clears the THRE interrupt + // (it will re-arm automatically the next time the TX FIFO drains to empty). + // ------------------------------------------------------------------- + case 2: + // Refresh IIR before returning it + updateIIR(); + *value = IIR; + retVal = 1; + + // If THRE was the reported interrupt, clear it; it re-arms on next drain + if ((IIR & 0x0E) == IIR_THR_EMPTY) { + irqThrEmpty = false; + updateIIR(); + } + break; + + // ------------------------------------------------------------------- + // Offset 3 – Line Control Register + // ------------------------------------------------------------------- + case 3: + *value = LCR; + retVal = 1; + break; + + // ------------------------------------------------------------------- + // Offset 4 – Modem Control Register + // ------------------------------------------------------------------- + case 4: + *value = MCR & 0x1F; // bits 7:5 are not implemented + retVal = 1; + break; + + // ------------------------------------------------------------------- + // Offset 5 – Line Status Register + // + // Reading LSR: + // • Reports live FIFO state (DR, THRE, TEMT, RXFE) + // • Clears the Receiver Line Status interrupt and 'sticky' OE/PE/FE/BI bits + // ------------------------------------------------------------------- + case 5: + *value = computeLSR(); + retVal = 1; + // Clear 'sticky' error bits and the line-status interrupt + LSR &= ~(LSR_OE | LSR_PE | LSR_FE | LSR_BI); + irqRxLineStatus = false; + updateIIR(); + break; + + // ------------------------------------------------------------------- + // Offset 6 – Modem Status Register + // + // Reading MSR: + // • Reports current CTS/DSR and delta bits + // • Clears delta bits (DCTS/DDSR/TERI/DDCD) and the modem-status interrupt + // ------------------------------------------------------------------- + case 6: + // Bits 7:4 are current modem input states (already maintained in MSR) + // Bits 3:0 are delta bits – cleared on read + *value = MSR; + retVal = MSR; + MSR &= 0xF0; // clear all delta bits + irqModemStatus = false; + updateIIR(); + break; + + // ------------------------------------------------------------------- + // Offset 7 – Scratch Register + // ------------------------------------------------------------------- + case 7: + *value = scratchReg; + retVal = 1; + break; + + default: + retVal = -2; + break; + } + return retVal; } +// =========================================================================== +// reconfigureSerial +// +// Closes and re-opens the host serial port with the settings currently +// encoded in LCR and requestedDivisor. Restarts the threadCycleUpdate thread. +// =========================================================================== void serialuartTL16C2550::reconfigureSerial(){ - - //This may get called a lot depending on the calling program and how it was written. - //Every change to LCR or MCR constitutes a reconfiguration of the hardware settings. - //if(!has_uart1) - //return; - - //Serial Port Configuration enumerators - SerialDataBits Databits; - SerialStopBits Stopbits; - SerialParity Parity; - - int wordLength=0; - - if(this->serialPort.isDeviceOpen()){ - // Close the serial device in reset or re-initilization - this->serialPort.closeDevice(); - } - - //Handle Databits - wordLength = (LCR&0x03); - switch( wordLength ){ - case 0: - Databits = SERIAL_DATABITS_5; - break; - case 1: - Databits = SERIAL_DATABITS_6; - break; - case 2: - Databits = SERIAL_DATABITS_7; - break; - case 3: - Databits = SERIAL_DATABITS_8; - break; - default: - Databits = SERIAL_DATABITS_8; - break; - } - - //Handle Stop Bits - if (LCR & 0x04){ - if(wordLength==5){ - Stopbits = SERIAL_STOPBITS_1_5; - } else { - Stopbits = SERIAL_STOPBITS_2; - } - } else { - Stopbits = SERIAL_STOPBITS_1; - } - - //Handle Parity - if( LCR & 0x08 ){ //Bit 3 Enabled - if( LCR & 0x20){ //Bit 5 Enabled - if( LCR & 0x10 ){ //Bit 4 Enabled - Parity = SERIAL_PARITY_SPACE; - } - else { //Bit 4 Disabled - Parity = SERIAL_PARITY_MARK; - } - } - else { //Bit 5 disabled - if( LCR & 0x10 ){ - Parity = SERIAL_PARITY_EVEN; - } else { - Parity = SERIAL_PARITY_ODD; - } - } - } - else { - Parity = SERIAL_PARITY_NONE; - } - - //Connect to real serial port - //int errorOpening = serialPort.openDevice(path.c_str(), baudCalculator(requestedDivisor)); - - int errorOpening = this->serialPort.openDevice(path.c_str(), baudCalculator(requestedDivisor), - Databits, - Parity, - Stopbits); - - //DTR is alreasy ready when port is open - this->serialPort.DTR(MCR & 0x01); - //RTS is controlled from AFE bitof MCR (always ready if flow control is enabled - this->serialPort.RTS( MCR & 0x20 ); - - // If connection fails, return the error code and display a success message - if (errorOpening!=1){ - std::cerr<(Parity)); - std::cerr<<"\tStopbits: "<(Stopbits)); - std::cerr<<"\tDivisor: "<path<(Parity))<(Stopbits))<serialPort.isDeviceOpen()) { + this->quit = true; + if (t.joinable()) t.join(); + this->quit = false; + this->serialPort.closeDevice(); + } + + // Data bits + switch (wordLength) { + case 0: Databits = SERIAL_DATABITS_5; break; + case 1: Databits = SERIAL_DATABITS_6; break; + case 2: Databits = SERIAL_DATABITS_7; break; + default: Databits = SERIAL_DATABITS_8; break; + } + + // Stop bits + if (LCR & 0x04) { + Stopbits = (wordLength == 0) ? SERIAL_STOPBITS_1_5 : SERIAL_STOPBITS_2; + } else { + Stopbits = SERIAL_STOPBITS_1; + } + + // Parity + if (LCR & 0x08) { + if (LCR & 0x20) { + Parity = (LCR & 0x10) ? SERIAL_PARITY_SPACE : SERIAL_PARITY_MARK; + } else { + Parity = (LCR & 0x10) ? SERIAL_PARITY_EVEN : SERIAL_PARITY_ODD; + } + } else { + Parity = SERIAL_PARITY_NONE; + } + + unsigned int baudRate = baudCalculator(requestedDivisor); + int errorOpening = this->serialPort.openDevice(path.c_str(), baudRate, Databits, Parity, Stopbits); + this->serialPort.DTR(MCR & MCR_DTR); + this->serialPort.RTS(MCR & MCR_AFE); // AFE controls RTS dynamically + + if (errorOpening != 1) { + std::cerr << errorOpening << ": Error opening: " << path << std::endl; + switch (errorOpening) { + case -1: std::cerr << "device not found\n"; break; + case -2: std::cerr << "error while opening the device\n"; break; + case -3: std::cerr << "error while getting port parameters\n"; break; + case -4: std::cerr << "Speed (Bauds) not recognized\n"; break; + case -5: std::cerr << "error while writing port parameters\n"; break; + case -6: std::cerr << "error while writing timeout parameters\n"; break; + case -7: std::cerr << "Databits not recognized\n"; break; + case -8: std::cerr << "Stopbits not recognized\n"; break; + case -9: std::cerr << "Parity not recognized\n"; break; + default: break; + } + } else { + std::cerr << "LCR: $" << std::hex << (int)LCR << std::dec + << "\t Baud: " << baudRate + << "\tData bits: " << 5 + (int)Databits + << "\tParity: " << descParity(static_cast(Parity)) + << "\tStopbits: " << descStopbits(static_cast(Stopbits)) + << "\tDivisor: " << requestedDivisor + << std::endl; + } + + // Update baud-rate timing for threadCycleUpdate + baud_delay = std::chrono::nanoseconds((1000000000 / baudRate) / checksperbaud); + + // ticksPerChar: at checksperbaud ticks-per-baud-event, + // one character ≈ (bits_per_char) ticks. Use 10 for 8N1. + ticksPerChar = checksperbaud; + rxTimeoutThreshold = RX_TIMEOUT_CHARS * ticksPerChar; + + // Restart the background thread + t = std::thread(&serialuartTL16C2550::threadCycleUpdate, this); } -int serialuartTL16C2550::init(char* port) { - - //if(!has_uart1) - //return -2; - /* - * ROMTERM.PRG v1.27 reads three addresses at start-up - * $9fe1 == 0 [IER] - * $9fe3 == 0 [LCR] - * $9fe4 == 0 [MCR] - * $9fe7 //Scratch register writable check - * These must be zero or it doesn't think there is a card installed - */ - IER = 0; - IIR = 0; - FCR = 0; - MCR = 0; - LCR = 0; //1-stop, no parity, 8-bit word at start-up - LSR = 0; - MSR = 0; - DLSB= 8; //Default 115200 at start-up - DMSB= 0; - requestedDivisor= 8; - - // Close the serial device in reset or initilization - if(this->serialPort.isDeviceOpen()){ - // Close the serial device in reset or initilization - this->serialPort.closeDevice(); - } - - - // Connection to serial port -#if defined (_WIN32) || defined( _WIN64) - //Windows can't open COM ports greater than COM9 without this. - path ="\\\\.\\\\"; +// =========================================================================== +// initialization +// =========================================================================== +int serialuartTL16C2550::init(char *port){ + // Power-on defaults (for instance -> ROMTERM.PRG checks IER/LCR/MCR = 0, and scratch register write, to detect the card) + IIR = IIR_NO_INT; + IER = 0; + FCR = 0; + MCR = 0; + LCR = 0; + LSR = LSR_THRE | LSR_TEMT; + MSR = 0; + DLSB = 0x60; // default on emulator launch/reset: 9600 baud with 14.7456 MHz crystal + DMSB = 0; + requestedDivisor = 0x60; + loopvalue = 0; + + irqRxLineStatus = false; + irqRxDataReady = false; + irqCharTimeout = false; + irqThrEmpty = false; + irqModemStatus = false; + + rxTimeoutTicks = 0; + ticksPerChar = checksperbaud; + rxTimeoutThreshold = RX_TIMEOUT_CHARS * ticksPerChar; + prevCTS = false; + prevDSR = false; + hasCTS = false; + hasDSR = false; + availCount = 0; + fifo_level = 1; + + {//Block Scope + std::lock_guard lk(rxMutex); + rxFifo.clear(); + }//mutex released + {//Block Scope + std::lock_guard lk(txMutex); + txFifo.clear(); + }//mutex released + + if (this->serialPort.isDeviceOpen()) { + this->quit = true; + if (t.joinable()) t.join(); + this->quit = false; + this->serialPort.closeDevice(); + } + + // Build the path string (Windows COM >9 requires \\.\\ prefix) +#if defined(_WIN32) || defined(_WIN64) + path = "\\\\.\\"; #else - path =""; + path = ""; #endif - - path += (char*)port; - std::cout<<"Attempting: "<serialPort.openDevice(path.c_str(), 115200); - - // If connection fails, return the error code and display a success message - if (errorOpening!=1){ - std::cout<<(int)errorOpening<<": "; - switch(errorOpening){ - case -1: - std::cerr<<"device not found\n"; - break; - case -2: - std::cerr<<"error while opening the device\n"; - break; - case -3: - std::cerr<<"error while getting port parameters\n"; - break; - case -4: - std::cerr<<"Speed (Bauds) not recognized\n"; - break; - case -5: - std::cerr<<"error while writing port parameters\n"; - break; - case -6: - std::cerr<<"error while writing timeout parameters\n"; - break; - case -7: - std::cerr<<"Databits not recognized\n"; - break; - case -8: - std::cerr<<"Stopbits not recognized\n"; - break; - case -9: - std::cerr<<"Parity not recognized\n"; - break; - default: - std::cout<<": Error opening: "<serialPort.DTR(false); - this->serialPort.RTS(false); - - return 0; + path += (char *)port; + + std::cout << "Attempting: " << path << ", "; + int errorOpening = this->serialPort.openDevice(path.c_str(), 9600); + + if (errorOpening != 1) { + std::cout << (int)errorOpening << ": "; + switch (errorOpening) { + case -1: std::cerr << "device not found\n"; break; + case -2: std::cerr << "error while opening the device\n"; break; + case -3: std::cerr << "error while getting port parameters\n"; break; + case -4: std::cerr << "Speed (Bauds) not recognized\n"; break; + case -5: std::cerr << "error while writing port parameters\n"; break; + case -6: std::cerr << "error while writing timeout parameters\n"; break; + case -7: std::cerr << "Databits not recognized\n"; break; + case -8: std::cerr << "Stopbits not recognized\n"; break; + case -9: std::cerr << "Parity not recognized\n"; break; + default: std::cout << ": Error opening: " << path << std::endl; break; + } + return -1; + } + + printf("Successful connection to %s\n", path.c_str()); + this->serialPort.clearDTR(); + this->serialPort.clearRTS(); + + baud_delay = std::chrono::nanoseconds((1000000000 / 9600) / checksperbaud); + + t = std::thread(&serialuartTL16C2550::threadCycleUpdate, this); + return 0; } -int serialuartTL16C2550::write(unsigned char* val){ - //Write one byte at a time. - //FIFO possible, but wouldn't match speed of hardware. - int retVal= this->serialPort.writeBytes(val,1); - return retVal; +// =========================================================================== +// Low-level port helpers +// =========================================================================== + +int serialuartTL16C2550::write(unsigned char *val){ + return this->serialPort.writeBytes(val, 1); } -int serialuartTL16C2550::read(unsigned char* readVal){ - //Read one byte at a time... Only way to mimic hardware. - return this->serialPort.readBytes(readVal,1,1000,1000); +int serialuartTL16C2550::read(unsigned char *readVal){ + return this->serialPort.readBytes(readVal, 1, 1000, 1000); } -int serialuartTL16C2550::dataAvailable(){ - //Bytes available from computer UART - return this->serialPort.available(); +int serialuartTL16C2550::dataAvailable(){ + return this->serialPort.available(); } int serialuartTL16C2550::baudCalculator(uint16_t divisor){ - if(divisor > 0) - return ( (OSC/divisor) / 16 ); - else - return -1; + if (divisor > 0) + return (OSC / divisor) / 16; + return -1; } +// =========================================================================== +// Diagnostic helpers +// =========================================================================== + std::string serialuartTL16C2550::descParity(int parity){ - - std::string results; - - switch(parity){ - case 0: - results="No Parity"; - break; - case 1: - results="Even Parity"; - break; - case 2: - results="Odd Parity"; - break; - case 3: - results="Mark Parity"; - break; - case 4: - results="Space Parity"; - break; - default: - results="Error"; - break; - } - return results; + switch (parity) { + case 0: return "No Parity"; + case 1: return "Even Parity"; + case 2: return "Odd Parity"; + case 3: return "Mark Parity"; + case 4: return "Space Parity"; + default: return "Error"; + } } std::string serialuartTL16C2550::descStopbits(int stopbits){ - std::string results; - - switch(stopbits){ - case 0: - results="1 bit"; - break; - case 1: - results="1.5 bits"; - break; - case 2: - results="2 bits"; - break; - default: - results="Error"; - break; - } - return results; + switch (stopbits) { + case 0: return "1 bit"; + case 1: return "1.5 bits"; + case 2: return "2 bits"; + default: return "Error"; + } } diff --git a/src/extern/serialuart/serialuartTL16C2550.hpp b/src/extern/serialuart/serialuartTL16C2550.hpp index 9b02a9d9..69014102 100644 --- a/src/extern/serialuart/serialuartTL16C2550.hpp +++ b/src/extern/serialuart/serialuartTL16C2550.hpp @@ -2,9 +2,9 @@ \file serialuartTL16C2550.hpp \brief Header file of the class serialuartTL16C2550. A serial UART for Commander X16 Emulator \author Jason Hill -\version 0.1 +\version 0.3 \date September 18th of 2025, by Jason Hill -\modified none +\modified March 2026 – Added 16-byte RX/TX FIFOs, threading, and full TL16C2550 interrupt system This Serial library is used for communication of a physical serial device on the X16 emulator. Simulating some aspects of the TL16C2550 for use on personal computers. @@ -23,42 +23,204 @@ This is a licence-free software, it can be used by anyone who try to build a bet #include #include #include +#include +#include +#include +#include #include "serialib/serialib.h" -extern unsigned char scratchReg; -extern bool has_uart1; -extern bool has_uart2; - -class serialuartTL16C2550{ - public: - - //serialuartTL16C2550(); - ~serialuartTL16C2550(); - int init(char*); - int addrwrite(unsigned char *value, int address); - int addrread(unsigned char* value, int address); - - private: - //UART Registers - unsigned char IIR, IER, - FCR, MCR, LCR, LSR, MSR, - scratchReg, - DLSB, DMSB, - loopvalue; - uint16_t requestedDivisor; - serialib serialPort; - - //Crystal Oscilator Speed on Texelec Serial/Wifi Card - static const int OSC = 14745600; - std::string path; - void reconfigureSerial(); - int write(unsigned char*); - int read( unsigned char*); - int dataAvailable(); - int baudCalculator(uint16_t); - - //For easy descriptions on errors - std::string descParity(int); - std::string descStopbits(int); - +// --------------------------------------------------------------------------- +// Hardware FIFO capacity for the TL16C2550 +// --------------------------------------------------------------------------- +static constexpr int UART_HW_FIFO_SIZE = 16; + +// --------------------------------------------------------------------------- +// RX FIFO entry: each byte in the FIFO carries its own per-character error +// flags exactly as the TL16C2550 stores them. The flags occupy LSR bits +// 1-4 (OE/PE/FE/BI) and are reported in LSR only when that character is at +// the head of the FIFO. +// --------------------------------------------------------------------------- +struct UartRxEntry { + unsigned char data; + unsigned char err; // LSR error bits for this character: OE|PE|FE|BI +}; + +// --------------------------------------------------------------------------- +// IIR interrupt-type codes (bits 3:1). Bit 0 = 0 means interrupt pending. +// --------------------------------------------------------------------------- +static constexpr unsigned char IIR_NO_INT = 0x01; // no interrupt pending +static constexpr unsigned char IIR_MODEM_STATUS = 0x00; // priority 5 (lowest) +static constexpr unsigned char IIR_THR_EMPTY = 0x02; // priority 4 +static constexpr unsigned char IIR_RX_DATA = 0x04; // priority 2 +static constexpr unsigned char IIR_RX_LINE_STAT = 0x06; // priority 1 (highest) +static constexpr unsigned char IIR_CHAR_TIMEOUT = 0x0C; // priority 3 (FIFO mode only) +static constexpr unsigned char IIR_FIFO_BITS = 0xC0; // bits 7:6 when FIFOs enabled + +// --------------------------------------------------------------------------- +// LSR bit masks +// --------------------------------------------------------------------------- +static constexpr unsigned char LSR_DR = 0x01; // data ready +static constexpr unsigned char LSR_OE = 0x02; // overrun error +static constexpr unsigned char LSR_PE = 0x04; // parity error +static constexpr unsigned char LSR_FE = 0x08; // framing error +static constexpr unsigned char LSR_BI = 0x10; // break interrupt +static constexpr unsigned char LSR_THRE = 0x20; // transmitter holding register empty +static constexpr unsigned char LSR_TEMT = 0x40; // transmitter empty (shift reg + holding) +static constexpr unsigned char LSR_RXFE = 0x80; // error in RX FIFO (FIFO mode only) + +// --------------------------------------------------------------------------- +// IER bit masks +// --------------------------------------------------------------------------- +static constexpr unsigned char IER_ERBFI = 0x01; // enable RX data available interrupt +static constexpr unsigned char IER_ETBEI = 0x02; // enable TX holding register empty interrupt +static constexpr unsigned char IER_ELSI = 0x04; // enable receiver line status interrupt +static constexpr unsigned char IER_EDSSI = 0x08; // enable modem status interrupt + +// --------------------------------------------------------------------------- +// FCR bit masks +// --------------------------------------------------------------------------- +static constexpr unsigned char FCR_FIFO_EN = 0x01; +static constexpr unsigned char FCR_RX_RESET = 0x02; +static constexpr unsigned char FCR_TX_RESET = 0x04; +static constexpr unsigned char FCR_RX_TRIG = 0xC0; // bits 7:6 select trigger level + +// --------------------------------------------------------------------------- +// MCR bit masks +// --------------------------------------------------------------------------- +static constexpr unsigned char MCR_DTR = 0x01; +static constexpr unsigned char MCR_RTS = 0x02; +static constexpr unsigned char MCR_OUT1 = 0x04; +static constexpr unsigned char MCR_OUT2 = 0x08; // gates IRQ output to system +static constexpr unsigned char MCR_LOOPBACK = 0x10; +static constexpr unsigned char MCR_AFE = 0x20; // Auto Flow-control Enable + +// Number of character-times of RX FIFO inactivity before timeout interrupt +static constexpr int RX_TIMEOUT_CHARS = 4; + +class serialuartTL16C2550 { +public: + serialuartTL16C2550(); + ~serialuartTL16C2550(); + + int init(char*); + int addrwrite(unsigned char *value, int address); + int addrread (unsigned char *value, int address); + + // Returns true when the UART is asserting its IRQ output line. + // MCR bit 3 (OUT2) must be set for IRQ to be driven. + // The X16 emulator will poll this to know whether to assert the CPU's IRQ pin. + bool irqPending() const; + +private: + // ---- TL16C2550 Addressable Registers ---------------------------------------- + unsigned char IIR; // Interrupt Identification Register (read-only) + unsigned char IER; // Interrupt Enable Register + unsigned char FCR; // FIFO Control Register (write-only; shadow kept here) + unsigned char MCR; // Modem Control Register + unsigned char LCR; // Line Control Register + unsigned char LSR; // Line Status Register (partially computed on read) + unsigned char MSR; // Modem Status Register + unsigned char scratchReg; // Scratch Register (no side effects) + unsigned char DLSB; // Divisor Latch – Least Significant Byte (DLAB=1) + unsigned char DMSB; // Divisor Latch – Most Significant Byte (DLAB=1) + uint16_t requestedDivisor; + + // Loopback data register: THR writes -> here; RBR reads <- here in loopback mode + unsigned char loopvalue; + + // ---- 16-Byte RX FIFO -------------------------------------------------------- + // Stores received characters together with their per-character error flags. + // Protected by rxMutex because threadCycleUpdate() pushes from a background thread + // while addrread() pops from the emulator thread. + std::deque rxFifo; + std::mutex rxMutex; + + // ---- 16-Byte TX FIFO -------------------------------------------------------- + // The emulator thread pushes via addrwrite(); threadCycleUpdate() drains to the host + // serial port. + std::deque txFifo; + std::mutex txMutex; + + // ---- Interrupt Pending Flags ------------------------------------------------- + // Each flag is set by the event that causes the condition and cleared when the + // condition is resolved (see TL16C2550 datasheet Table 3 for clearing actions). + bool irqRxLineStatus; // OE/PE/FE/BI in head of FIFO; cleared by LSR read + bool irqRxDataReady; // FIFO at or above trigger level; cleared when FIFO falls below + bool irqCharTimeout; // FIFO non-empty, idle > 4 char-times; cleared by RBR read + bool irqThrEmpty; // TX FIFO empty; cleared by THR write or IIR read + bool irqModemStatus; // CTS/DSR/RI/DCD changed; cleared by MSR read + + // ---- Character-Timeout Tracking --------------------------------------------- + // threadCycleUpdate increments rxTimeoutTicks every tick when RX FIFO is non-empty + // and no new byte arrived. When it reaches rxTimeoutThreshold the timeout + // interrupt fires. Any new RX byte resets the counter. + int rxTimeoutTicks; + int rxTimeoutThreshold; // = RX_TIMEOUT_CHARS * ticks_per_char, updated on baud change + + // Ticks per character at the current baud/format (used to compute threshold) + int ticksPerChar; + + // ---- Modem Signal Tracking -------------------------------------------------- + // Previous states of CTS/DSR, so we can detect transitions for MSR delta bits. + bool prevCTS; + bool prevDSR; + + // ---- Physical Serial Port --------------------------------------------------- + serialib serialPort; + + // Crystal Oscillator Speed on Texelec Serial/Wifi Card + static const int OSC = 14745600; + std::string path; + + // ---- Private Helper Methods ------------------------------------------------- + void reconfigureSerial(); + int write(unsigned char*); + int read (unsigned char*); + int dataAvailable(); + int baudCalculator(uint16_t); + + // Rebuild IIR from the five interrupt-pending flags and IER. + // Call whenever any flag or IER changes. + void updateIIR(); + + // Push a received byte (plus error flags) into rxFifo. + // Handles overrun: if FIFO is full, sets OE and fires line-status interrupt. + void rxFifoPush(unsigned char data, unsigned char errFlags = 0); + + // Pop the head entry from rxFifo. Returns false if empty. + bool rxFifoPop(UartRxEntry &out); + + // Reset (flush) the RX FIFO and clear related interrupt state (FCR bit 1). + void rxFifoReset(); + + // Reset (flush) the TX FIFO and arm THRE interrupt (FCR bit 2). + void txFifoReset(); + + // Recompute the LSR byte from live FIFO state. + // Sticky error bits (OE/PE/FE/BI) are held in the LSR member until LSR is read. + unsigned char computeLSR() const; + + // Update MSR delta bits when CTS/DSR transitions are detected. + void updateMSR(bool newCTS, bool newDSR); + + // ---- Background Thread ------------------------------------------------------ + std::thread t; + bool quit; + void threadCycleUpdate(); // thread body + + // Cached signal states (set by threadCycleUpdate, read by addrwrite/addrread) + bool hasCTS; + bool hasDSR; + int availCount; // host OS bytes available in its own serial buffer + + //Baud-rate timing + //checks-per-baud: How often the thread will scan the UART, at least twice as fast as baud rate, for no delays + //see initilization list for class. + int checksperbaud; + unsigned char fifo_level; // RX trigger level decoded from FCR (1/4/8/14) + std::chrono::steady_clock::duration baud_delay; + + // Diagnostic helpers + std::string descParity(int); + std::string descStopbits(int); }; diff --git a/src/extern/serialuart/serialuart_wrapper.cpp b/src/extern/serialuart/serialuart_wrapper.cpp index e940db2a..cf69bfc0 100644 --- a/src/extern/serialuart/serialuart_wrapper.cpp +++ b/src/extern/serialuart/serialuart_wrapper.cpp @@ -26,6 +26,16 @@ serialuartTL16C2550Handle uartCreate() { return static_cast(new serialuartTL16C2550()); } +bool uart_irqPending( serialuartTL16C2550Handle uart){ + serialuartTL16C2550* obj = static_cast(uart); + + if (obj != NULL) { + return obj->irqPending(); + } + + return false; +} + int uart_init( serialuartTL16C2550Handle uart, char *value ) { serialuartTL16C2550* obj = static_cast(uart); diff --git a/src/extern/serialuart/serialuart_wrapper.h b/src/extern/serialuart/serialuart_wrapper.h index 60ae2037..303d37b5 100644 --- a/src/extern/serialuart/serialuart_wrapper.h +++ b/src/extern/serialuart/serialuart_wrapper.h @@ -32,6 +32,7 @@ serialuartTL16C2550Handle uartCreate(); int uart_init( serialuartTL16C2550Handle, char*); int uart_addrwrite( serialuartTL16C2550Handle , unsigned char*, int); int uart_addrread( serialuartTL16C2550Handle , unsigned char*, int); +bool uart_irqPending( serialuartTL16C2550Handle uart); // Destructor-like function diff --git a/src/main.c b/src/main.c index 971ff094..e0c74588 100644 --- a/src/main.c +++ b/src/main.c @@ -1215,7 +1215,7 @@ main(int argc, char **argv) if (argc && argv[0][0] != '-') { uart1_addr = 0x9f00 | ((uint16_t)strtol(argv[0], NULL, 16) & 0xff); - uart1_addr &= 0xfff0; + //uart1_addr &= 0xfff0; argc--; argv++; } else { @@ -1865,7 +1865,7 @@ emulator_loop(void *param) audio_render(); } - if (video_get_irq_out() || via1_irq() || (has_via2 && via2_irq()) || (ym2151_irq_support && YM_irq()) || (has_midi_card && midi_serial_irq())) { + if (video_get_irq_out() || via1_irq() || (has_via2 && via2_irq()) || (ym2151_irq_support && YM_irq()) || (has_midi_card && midi_serial_irq()) || (has_uart1 && uart_irqPending(uart1)) || (has_uart2 && uart_irqPending(uart2)) ) { // printf("IRQ!\n"); irq6502(); } From 907ce4b46cbeca8a8770a9490e7fa95a0f661207 Mon Sep 17 00:00:00 2001 From: morphinejh <78104266+morphinejh@users.noreply.github.com> Date: Tue, 12 May 2026 19:39:37 -0400 Subject: [PATCH 4/7] added back mnemonics.h and tables.h --- src/cpu/mnemonics.h | 579 ++++++++++++++++++++++++++++++++++++++++++++ src/cpu/tables.h | 121 +++++++++ 2 files changed, 700 insertions(+) create mode 100644 src/cpu/mnemonics.h create mode 100644 src/cpu/tables.h diff --git a/src/cpu/mnemonics.h b/src/cpu/mnemonics.h new file mode 100644 index 00000000..2e6ccda5 --- /dev/null +++ b/src/cpu/mnemonics.h @@ -0,0 +1,579 @@ +/* Generated by buildtables.py */ + +static const char *mnemonics_c02[256] = { + // $0X + /* $00 */ "brk #$%02x", + /* $01 */ "ora ($%02x,x)", + /* $02 */ "nop #$%02x", + /* $03 */ "nop ", + /* $04 */ "tsb $%02x", + /* $05 */ "ora $%02x", + /* $06 */ "asl $%02x", + /* $07 */ "rmb0 $%02x", + /* $08 */ "php ", + /* $09 */ "ora #$%%0%hhux", + /* $0A */ "asl a", + /* $0B */ "nop ", + /* $0C */ "tsb $%04x", + /* $0D */ "ora $%04x", + /* $0E */ "asl $%04x", + /* $0F */ "bbr0 $%02x, $%04x", + + // $1X + /* $10 */ "bpl $%02x", + /* $11 */ "ora ($%02x),y", + /* $12 */ "ora ($%02x)", + /* $13 */ "nop ", + /* $14 */ "trb $%02x", + /* $15 */ "ora $%02x,x", + /* $16 */ "asl $%02x,x", + /* $17 */ "rmb1 $%02x", + /* $18 */ "clc ", + /* $19 */ "ora $%04x,y", + /* $1A */ "inc a", + /* $1B */ "nop ", + /* $1C */ "trb $%04x", + /* $1D */ "ora $%04x,x", + /* $1E */ "asl $%04x,x", + /* $1F */ "bbr1 $%02x, $%04x", + + // $2X + /* $20 */ "jsr $%04x", + /* $21 */ "and ($%02x,x)", + /* $22 */ "nop #$%02x", + /* $23 */ "nop ", + /* $24 */ "bit $%02x", + /* $25 */ "and $%02x", + /* $26 */ "rol $%02x", + /* $27 */ "rmb2 $%02x", + /* $28 */ "plp ", + /* $29 */ "and #$%%0%hhux", + /* $2A */ "rol a", + /* $2B */ "nop ", + /* $2C */ "bit $%04x", + /* $2D */ "and $%04x", + /* $2E */ "rol $%04x", + /* $2F */ "bbr2 $%02x, $%04x", + + // $3X + /* $30 */ "bmi $%02x", + /* $31 */ "and ($%02x),y", + /* $32 */ "and ($%02x)", + /* $33 */ "nop ", + /* $34 */ "bit $%02x,x", + /* $35 */ "and $%02x,x", + /* $36 */ "rol $%02x,x", + /* $37 */ "rmb3 $%02x", + /* $38 */ "sec ", + /* $39 */ "and $%04x,y", + /* $3A */ "dec a", + /* $3B */ "nop ", + /* $3C */ "bit $%04x,x", + /* $3D */ "and $%04x,x", + /* $3E */ "rol $%04x,x", + /* $3F */ "bbr3 $%02x, $%04x", + + // $4X + /* $40 */ "rti ", + /* $41 */ "eor ($%02x,x)", + /* $42 */ "nop #$%02x", + /* $43 */ "nop ", + /* $44 */ "nop #$%02x", + /* $45 */ "eor $%02x", + /* $46 */ "lsr $%02x", + /* $47 */ "rmb4 $%02x", + /* $48 */ "pha ", + /* $49 */ "eor #$%%0%hhux", + /* $4A */ "lsr a", + /* $4B */ "nop ", + /* $4C */ "jmp $%04x", + /* $4D */ "eor $%04x", + /* $4E */ "lsr $%04x", + /* $4F */ "bbr4 $%02x, $%04x", + + // $5X + /* $50 */ "bvc $%02x", + /* $51 */ "eor ($%02x),y", + /* $52 */ "eor ($%02x)", + /* $53 */ "nop ", + /* $54 */ "nop #$%02x", + /* $55 */ "eor $%02x,x", + /* $56 */ "lsr $%02x,x", + /* $57 */ "rmb5 $%02x", + /* $58 */ "cli ", + /* $59 */ "eor $%04x,y", + /* $5A */ "phy ", + /* $5B */ "nop ", + /* $5C */ "nop ", + /* $5D */ "eor $%04x,x", + /* $5E */ "lsr $%04x,x", + /* $5F */ "bbr5 $%02x, $%04x", + + // $6X + /* $60 */ "rts ", + /* $61 */ "adc ($%02x,x)", + /* $62 */ "nop #$%02x", + /* $63 */ "nop ", + /* $64 */ "stz $%02x", + /* $65 */ "adc $%02x", + /* $66 */ "ror $%02x", + /* $67 */ "rmb6 $%02x", + /* $68 */ "pla ", + /* $69 */ "adc #$%%0%hhux", + /* $6A */ "ror a", + /* $6B */ "nop ", + /* $6C */ "jmp ($%04x)", + /* $6D */ "adc $%04x", + /* $6E */ "ror $%04x", + /* $6F */ "bbr6 $%02x, $%04x", + + // $7X + /* $70 */ "bvs $%02x", + /* $71 */ "adc ($%02x),y", + /* $72 */ "adc ($%02x)", + /* $73 */ "nop ", + /* $74 */ "stz $%02x,x", + /* $75 */ "adc $%02x,x", + /* $76 */ "ror $%02x,x", + /* $77 */ "rmb7 $%02x", + /* $78 */ "sei ", + /* $79 */ "adc $%04x,y", + /* $7A */ "ply ", + /* $7B */ "nop ", + /* $7C */ "jmp ($%04x,x)", + /* $7D */ "adc $%04x,x", + /* $7E */ "ror $%04x,x", + /* $7F */ "bbr7 $%02x, $%04x", + + // $8X + /* $80 */ "bra $%02x", + /* $81 */ "sta ($%02x,x)", + /* $82 */ "nop #$%02x", + /* $83 */ "nop ", + /* $84 */ "sty $%02x", + /* $85 */ "sta $%02x", + /* $86 */ "stx $%02x", + /* $87 */ "smb0 $%02x", + /* $88 */ "dey ", + /* $89 */ "bit #$%%0%hhux", + /* $8A */ "txa ", + /* $8B */ "nop ", + /* $8C */ "sty $%04x", + /* $8D */ "sta $%04x", + /* $8E */ "stx $%04x", + /* $8F */ "bbs0 $%02x, $%04x", + + // $9X + /* $90 */ "bcc $%02x", + /* $91 */ "sta ($%02x),y", + /* $92 */ "sta ($%02x)", + /* $93 */ "nop ", + /* $94 */ "sty $%02x,x", + /* $95 */ "sta $%02x,x", + /* $96 */ "stx $%02x,y", + /* $97 */ "smb1 $%02x", + /* $98 */ "tya ", + /* $99 */ "sta $%04x,y", + /* $9A */ "txs ", + /* $9B */ "nop ", + /* $9C */ "stz $%04x", + /* $9D */ "sta $%04x,x", + /* $9E */ "stz $%04x,x", + /* $9F */ "bbs1 $%02x, $%04x", + + // $AX + /* $A0 */ "ldy #$%%0%hhux", + /* $A1 */ "lda ($%02x,x)", + /* $A2 */ "ldx #$%%0%hhux", + /* $A3 */ "nop ", + /* $A4 */ "ldy $%02x", + /* $A5 */ "lda $%02x", + /* $A6 */ "ldx $%02x", + /* $A7 */ "smb2 $%02x", + /* $A8 */ "tay ", + /* $A9 */ "lda #$%%0%hhux", + /* $AA */ "tax ", + /* $AB */ "nop ", + /* $AC */ "ldy $%04x", + /* $AD */ "lda $%04x", + /* $AE */ "ldx $%04x", + /* $AF */ "bbs2 $%02x, $%04x", + + // $BX + /* $B0 */ "bcs $%02x", + /* $B1 */ "lda ($%02x),y", + /* $B2 */ "lda ($%02x)", + /* $B3 */ "nop ", + /* $B4 */ "ldy $%02x,x", + /* $B5 */ "lda $%02x,x", + /* $B6 */ "ldx $%02x,y", + /* $B7 */ "smb3 $%02x", + /* $B8 */ "clv ", + /* $B9 */ "lda $%04x,y", + /* $BA */ "tsx ", + /* $BB */ "nop ", + /* $BC */ "ldy $%04x,x", + /* $BD */ "lda $%04x,x", + /* $BE */ "ldx $%04x,y", + /* $BF */ "bbs3 $%02x, $%04x", + + // $CX + /* $C0 */ "cpy #$%%0%hhux", + /* $C1 */ "cmp ($%02x,x)", + /* $C2 */ "nop #$%02x", + /* $C3 */ "nop ", + /* $C4 */ "cpy $%02x", + /* $C5 */ "cmp $%02x", + /* $C6 */ "dec $%02x", + /* $C7 */ "smb4 $%02x", + /* $C8 */ "iny ", + /* $C9 */ "cmp #$%%0%hhux", + /* $CA */ "dex ", + /* $CB */ "wai ", + /* $CC */ "cpy $%04x", + /* $CD */ "cmp $%04x", + /* $CE */ "dec $%04x", + /* $CF */ "bbs4 $%02x, $%04x", + + // $DX + /* $D0 */ "bne $%02x", + /* $D1 */ "cmp ($%02x),y", + /* $D2 */ "cmp ($%02x)", + /* $D3 */ "nop ", + /* $D4 */ "nop #$%02x", + /* $D5 */ "cmp $%02x,x", + /* $D6 */ "dec $%02x,x", + /* $D7 */ "smb5 $%02x", + /* $D8 */ "cld ", + /* $D9 */ "cmp $%04x,y", + /* $DA */ "phx ", + /* $DB */ "dbg ", + /* $DC */ "nop ", + /* $DD */ "cmp $%04x,x", + /* $DE */ "dec $%04x,x", + /* $DF */ "bbs5 $%02x, $%04x", + + // $EX + /* $E0 */ "cpx #$%%0%hhux", + /* $E1 */ "sbc ($%02x,x)", + /* $E2 */ "nop #$%02x", + /* $E3 */ "nop ", + /* $E4 */ "cpx $%02x", + /* $E5 */ "sbc $%02x", + /* $E6 */ "inc $%02x", + /* $E7 */ "smb6 $%02x", + /* $E8 */ "inx ", + /* $E9 */ "sbc #$%%0%hhux", + /* $EA */ "nop ", + /* $EB */ "nop ", + /* $EC */ "cpx $%04x", + /* $ED */ "sbc $%04x", + /* $EE */ "inc $%04x", + /* $EF */ "bbs6 $%02x, $%04x", + + // $FX + /* $F0 */ "beq $%02x", + /* $F1 */ "sbc ($%02x),y", + /* $F2 */ "sbc ($%02x)", + /* $F3 */ "nop ", + /* $F4 */ "nop #$%02x", + /* $F5 */ "sbc $%02x,x", + /* $F6 */ "inc $%02x,x", + /* $F7 */ "smb7 $%02x", + /* $F8 */ "sed ", + /* $F9 */ "sbc $%04x,y", + /* $FA */ "plx ", + /* $FB */ "nop ", + /* $FC */ "nop ", + /* $FD */ "sbc $%04x,x", + /* $FE */ "inc $%04x,x", + /* $FF */ "bbs7 $%02x, $%04x"}; + +static const char *mnemonics_c816[256] = { + // $0X + /* $00 */ "brk #$%02x", + /* $01 */ "ora ($%02x,x)", + /* $02 */ "cop #$%02x", + /* $03 */ "ora $%02x,S", + /* $04 */ "tsb $%02x", + /* $05 */ "ora $%02x", + /* $06 */ "asl $%02x", + /* $07 */ "ora [$%02x]", + /* $08 */ "php ", + /* $09 */ "ora #$%%0%hhux", + /* $0A */ "asl a", + /* $0B */ "phd ", + /* $0C */ "tsb $%04x", + /* $0D */ "ora $%04x", + /* $0E */ "asl $%04x", + /* $0F */ "ora $%06x", + + // $1X + /* $10 */ "bpl $%02x", + /* $11 */ "ora ($%02x),y", + /* $12 */ "ora ($%02x)", + /* $13 */ "ora ($%02x,S),y", + /* $14 */ "trb $%02x", + /* $15 */ "ora $%02x,x", + /* $16 */ "asl $%02x,x", + /* $17 */ "ora [$%02x],y", + /* $18 */ "clc ", + /* $19 */ "ora $%04x,y", + /* $1A */ "inc a", + /* $1B */ "tcs ", + /* $1C */ "trb $%04x", + /* $1D */ "ora $%04x,x", + /* $1E */ "asl $%04x,x", + /* $1F */ "ora $%06x,x", + + // $2X + /* $20 */ "jsr $%04x", + /* $21 */ "and ($%02x,x)", + /* $22 */ "jsl $%06x", + /* $23 */ "and $%02x,S", + /* $24 */ "bit $%02x", + /* $25 */ "and $%02x", + /* $26 */ "rol $%02x", + /* $27 */ "and [$%02x]", + /* $28 */ "plp ", + /* $29 */ "and #$%%0%hhux", + /* $2A */ "rol a", + /* $2B */ "pld ", + /* $2C */ "bit $%04x", + /* $2D */ "and $%04x", + /* $2E */ "rol $%04x", + /* $2F */ "and $%06x", + + // $3X + /* $30 */ "bmi $%02x", + /* $31 */ "and ($%02x),y", + /* $32 */ "and ($%02x)", + /* $33 */ "and ($%02x,S),y", + /* $34 */ "bit $%02x,x", + /* $35 */ "and $%02x,x", + /* $36 */ "rol $%02x,x", + /* $37 */ "and [$%02x],y", + /* $38 */ "sec ", + /* $39 */ "and $%04x,y", + /* $3A */ "dec a", + /* $3B */ "tsc ", + /* $3C */ "bit $%04x,x", + /* $3D */ "and $%04x,x", + /* $3E */ "rol $%04x,x", + /* $3F */ "and $%06x,x", + + // $4X + /* $40 */ "rti ", + /* $41 */ "eor ($%02x,x)", + /* $42 */ "wdm #$%02x", + /* $43 */ "eor $%02x,S", + /* $44 */ "mvp $%02x,$%02x", + /* $45 */ "eor $%02x", + /* $46 */ "lsr $%02x", + /* $47 */ "eor [$%02x]", + /* $48 */ "pha ", + /* $49 */ "eor #$%%0%hhux", + /* $4A */ "lsr a", + /* $4B */ "phk ", + /* $4C */ "jmp $%04x", + /* $4D */ "eor $%04x", + /* $4E */ "lsr $%04x", + /* $4F */ "eor $%06x", + + // $5X + /* $50 */ "bvc $%02x", + /* $51 */ "eor ($%02x),y", + /* $52 */ "eor ($%02x)", + /* $53 */ "eor ($%02x,S),y", + /* $54 */ "mvn $%02x,$%02x", + /* $55 */ "eor $%02x,x", + /* $56 */ "lsr $%02x,x", + /* $57 */ "eor [$%02x],y", + /* $58 */ "cli ", + /* $59 */ "eor $%04x,y", + /* $5A */ "phy ", + /* $5B */ "tcd ", + /* $5C */ "jml $%06x", + /* $5D */ "eor $%04x,x", + /* $5E */ "lsr $%04x,x", + /* $5F */ "eor $%06x,x", + + // $6X + /* $60 */ "rts ", + /* $61 */ "adc ($%02x,x)", + /* $62 */ "per $%04x", + /* $63 */ "adc $%02x,S", + /* $64 */ "stz $%02x", + /* $65 */ "adc $%02x", + /* $66 */ "ror $%02x", + /* $67 */ "adc [$%02x]", + /* $68 */ "pla ", + /* $69 */ "adc #$%%0%hhux", + /* $6A */ "ror a", + /* $6B */ "rtl ", + /* $6C */ "jmp ($%04x)", + /* $6D */ "adc $%04x", + /* $6E */ "ror $%04x", + /* $6F */ "adc $%06x", + + // $7X + /* $70 */ "bvs $%02x", + /* $71 */ "adc ($%02x),y", + /* $72 */ "adc ($%02x)", + /* $73 */ "adc ($%02x,S),y", + /* $74 */ "stz $%02x,x", + /* $75 */ "adc $%02x,x", + /* $76 */ "ror $%02x,x", + /* $77 */ "adc [$%02x],y", + /* $78 */ "sei ", + /* $79 */ "adc $%04x,y", + /* $7A */ "ply ", + /* $7B */ "tdc ", + /* $7C */ "jmp ($%04x,x)", + /* $7D */ "adc $%04x,x", + /* $7E */ "ror $%04x,x", + /* $7F */ "adc $%06x,x", + + // $8X + /* $80 */ "bra $%02x", + /* $81 */ "sta ($%02x,x)", + /* $82 */ "brl $%04x", + /* $83 */ "sta $%02x,S", + /* $84 */ "sty $%02x", + /* $85 */ "sta $%02x", + /* $86 */ "stx $%02x", + /* $87 */ "sta [$%02x]", + /* $88 */ "dey ", + /* $89 */ "bit #$%%0%hhux", + /* $8A */ "txa ", + /* $8B */ "phb ", + /* $8C */ "sty $%04x", + /* $8D */ "sta $%04x", + /* $8E */ "stx $%04x", + /* $8F */ "sta $%06x", + + // $9X + /* $90 */ "bcc $%02x", + /* $91 */ "sta ($%02x),y", + /* $92 */ "sta ($%02x)", + /* $93 */ "sta ($%02x,S),y", + /* $94 */ "sty $%02x,x", + /* $95 */ "sta $%02x,x", + /* $96 */ "stx $%02x,y", + /* $97 */ "sta [$%02x],y", + /* $98 */ "tya ", + /* $99 */ "sta $%04x,y", + /* $9A */ "txs ", + /* $9B */ "txy ", + /* $9C */ "stz $%04x", + /* $9D */ "sta $%04x,x", + /* $9E */ "stz $%04x,x", + /* $9F */ "sta $%06x,x", + + // $AX + /* $A0 */ "ldy #$%%0%hhux", + /* $A1 */ "lda ($%02x,x)", + /* $A2 */ "ldx #$%%0%hhux", + /* $A3 */ "lda $%02x,S", + /* $A4 */ "ldy $%02x", + /* $A5 */ "lda $%02x", + /* $A6 */ "ldx $%02x", + /* $A7 */ "lda [$%02x]", + /* $A8 */ "tay ", + /* $A9 */ "lda #$%%0%hhux", + /* $AA */ "tax ", + /* $AB */ "plb ", + /* $AC */ "ldy $%04x", + /* $AD */ "lda $%04x", + /* $AE */ "ldx $%04x", + /* $AF */ "lda $%06x", + + // $BX + /* $B0 */ "bcs $%02x", + /* $B1 */ "lda ($%02x),y", + /* $B2 */ "lda ($%02x)", + /* $B3 */ "lda ($%02x,S),y", + /* $B4 */ "ldy $%02x,x", + /* $B5 */ "lda $%02x,x", + /* $B6 */ "ldx $%02x,y", + /* $B7 */ "lda [$%02x],y", + /* $B8 */ "clv ", + /* $B9 */ "lda $%04x,y", + /* $BA */ "tsx ", + /* $BB */ "tyx ", + /* $BC */ "ldy $%04x,x", + /* $BD */ "lda $%04x,x", + /* $BE */ "ldx $%04x,y", + /* $BF */ "lda $%06x,x", + + // $CX + /* $C0 */ "cpy #$%%0%hhux", + /* $C1 */ "cmp ($%02x,x)", + /* $C2 */ "rep #$%02x", + /* $C3 */ "cmp $%02x,S", + /* $C4 */ "cpy $%02x", + /* $C5 */ "cmp $%02x", + /* $C6 */ "dec $%02x", + /* $C7 */ "cmp [$%02x]", + /* $C8 */ "iny ", + /* $C9 */ "cmp #$%%0%hhux", + /* $CA */ "dex ", + /* $CB */ "wai ", + /* $CC */ "cpy $%04x", + /* $CD */ "cmp $%04x", + /* $CE */ "dec $%04x", + /* $CF */ "cmp $%06x", + + // $DX + /* $D0 */ "bne $%02x", + /* $D1 */ "cmp ($%02x),y", + /* $D2 */ "cmp ($%02x)", + /* $D3 */ "cmp ($%02x,S),y", + /* $D4 */ "pei ($%02x)", + /* $D5 */ "cmp $%02x,x", + /* $D6 */ "dec $%02x,x", + /* $D7 */ "cmp [$%02x],y", + /* $D8 */ "cld ", + /* $D9 */ "cmp $%04x,y", + /* $DA */ "phx ", + /* $DB */ "dbg ", + /* $DC */ "jml [$%04x]", + /* $DD */ "cmp $%04x,x", + /* $DE */ "dec $%04x,x", + /* $DF */ "cmp $%06x,x", + + // $EX + /* $E0 */ "cpx #$%%0%hhux", + /* $E1 */ "sbc ($%02x,x)", + /* $E2 */ "sep #$%02x", + /* $E3 */ "sbc $%02x,S", + /* $E4 */ "cpx $%02x", + /* $E5 */ "sbc $%02x", + /* $E6 */ "inc $%02x", + /* $E7 */ "sbc [$%02x]", + /* $E8 */ "inx ", + /* $E9 */ "sbc #$%%0%hhux", + /* $EA */ "nop ", + /* $EB */ "xba ", + /* $EC */ "cpx $%04x", + /* $ED */ "sbc $%04x", + /* $EE */ "inc $%04x", + /* $EF */ "sbc $%06x", + + // $FX + /* $F0 */ "beq $%02x", + /* $F1 */ "sbc ($%02x),y", + /* $F2 */ "sbc ($%02x)", + /* $F3 */ "sbc ($%02x,S),y", + /* $F4 */ "pea #$%04x", + /* $F5 */ "sbc $%02x,x", + /* $F6 */ "inc $%02x,x", + /* $F7 */ "sbc [$%02x],y", + /* $F8 */ "sed ", + /* $F9 */ "sbc $%04x,y", + /* $FA */ "plx ", + /* $FB */ "xce ", + /* $FC */ "jsr ($%04x,x)", + /* $FD */ "sbc $%04x,x", + /* $FE */ "inc $%04x,x", + /* $FF */ "sbc $%06x,x"}; diff --git a/src/cpu/tables.h b/src/cpu/tables.h new file mode 100644 index 00000000..aacf120a --- /dev/null +++ b/src/cpu/tables.h @@ -0,0 +1,121 @@ +/* Generated by buildtables.py */ + +static void (*addrtable_c02[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ imp8, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 0 */ +/* 1 */ rel, indy, ind0, imp, zp, zpx, zpx, zp, imp, absy, acc, imp, abso, absx, absx,zprel, /* 1 */ +/* 2 */ abso, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 2 */ +/* 3 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, acc, imp, absx, absx, absx,zprel, /* 3 */ +/* 4 */ imp, indx, imm8, imp, imm8, zp, zp, zp, imp, immm, acc, imp, abso, abso, abso,zprel, /* 4 */ +/* 5 */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* 5 */ +/* 6 */ imp, indx, imm8, imp, zp, zp, zp, zp, imp, immm, acc, imp, ind, abso, abso,zprel, /* 6 */ +/* 7 */ rel, indy, ind0, imp, zpx, zpx, zpx, zp, imp, absy, imp, imp, ainx, absx, absx,zprel, /* 7 */ +/* 8 */ rel, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* 8 */ +/* 9 */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, abso, absx, absx,zprel, /* 9 */ +/* A */ immx, indx, immx, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* A */ +/* B */ rel, indy, ind0, imp, zpx, zpx, zpy, zp, imp, absy, imp, imp, absx, absx, absy,zprel, /* B */ +/* C */ immx, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* C */ +/* D */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel, /* D */ +/* E */ immx, indx, imm8, imp, zp, zp, zp, zp, imp, immm, imp, imp, abso, abso, abso,zprel, /* E */ +/* F */ rel, indy, ind0, imp, imm8, zpx, zpx, zp, imp, absy, imp, imp, imp, absx, absx,zprel /* F */ +}; + +static void (*addrtable_c816[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ imp8, indx, imp8, sr, zp, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 0 */ +/* 1 */ rel, indy, ind0,sridy, zp, zpx, zpx,indly, imp, absy, acc, imp, abso, absx, absx,abslx, /* 1 */ +/* 2 */ abso, indx, absl, sr, zp, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 2 */ +/* 3 */ rel, indy, ind0,sridy, zpx, zpx, zpx,indly, imp, absy, acc, imp, absx, absx, absx,abslx, /* 3 */ +/* 4 */ imp, indx, imm8, sr, bmv, zp, zp,indl0, imp, immm, acc, imp, abso, abso, abso, absl, /* 4 */ +/* 5 */ rel, indy, ind0,sridy, bmv, zpx, zpx,indly, imp, absy, imp, imp, absl, absx, absx,abslx, /* 5 */ +/* 6 */ imp, indx,rel16, sr, zp, zp, zp,indl0, imp, immm, acc, imp, ind, abso, abso, absl, /* 6 */ +/* 7 */ rel, indy, ind0,sridy, zpx, zpx, zpx,indly, imp, absy, imp, imp, ainx, absx, absx,abslx, /* 7 */ +/* 8 */ rel, indx,rel16, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* 8 */ +/* 9 */ rel, indy, ind0,sridy, zpx, zpx, zpy,indly, imp, absy, imp, imp, abso, absx, absx,abslx, /* 9 */ +/* A */ immx, indx, immx, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* A */ +/* B */ rel, indy, ind0,sridy, zpx, zpx, zpy,indly, imp, absy, imp, imp, absx, absx, absy,abslx, /* B */ +/* C */ immx, indx, imm8, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* C */ +/* D */ rel, indy, ind0,sridy,ind0p, zpx, zpx,indly, imp, absy, imp, imp,aindl, absx, absx,abslx, /* D */ +/* E */ immx, indx, imm8, sr, zp, zp, zp,indl0, imp, immm, imp, imp, abso, abso, abso, absl, /* E */ +/* F */ rel, indy, ind0,sridy,imm16, zpx, zpx,indly, imp, absy, imp, imp, ainx, absx, absx,abslx /* F */ +}; + +static void (*optable_c02[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ brk, ora, nop, nop, tsb, ora, asl, rmb0, php, ora, asl, nop, tsb, ora, asl, bbr0, /* 0 */ +/* 1 */ bpl, ora, ora, nop, trb, ora, asl, rmb1, clc, ora, inc, nop, trb, ora, asl, bbr1, /* 1 */ +/* 2 */ jsr, and, nop, nop, bit, and, rol, rmb2, plp, and, rol, nop, bit, and, rol, bbr2, /* 2 */ +/* 3 */ bmi, and, and, nop, bit, and, rol, rmb3, sec, and, dec, nop, bit, and, rol, bbr3, /* 3 */ +/* 4 */ rti, eor, nop, nop, nop, eor, lsr, rmb4, pha, eor, lsr, nop, jmp, eor, lsr, bbr4, /* 4 */ +/* 5 */ bvc, eor, eor, nop, nop, eor, lsr, rmb5, cli, eor, phy, nop, nop, eor, lsr, bbr5, /* 5 */ +/* 6 */ rts, adc, nop, nop, stz, adc, ror, rmb6, pla, adc, ror, nop, jmp, adc, ror, bbr6, /* 6 */ +/* 7 */ bvs, adc, adc, nop, stz, adc, ror, rmb7, sei, adc, ply, nop, jmp, adc, ror, bbr7, /* 7 */ +/* 8 */ bra, sta, nop, nop, sty, sta, stx, smb0, dey, bit, txa, nop, sty, sta, stx, bbs0, /* 8 */ +/* 9 */ bcc, sta, sta, nop, sty, sta, stx, smb1, tya, sta, txs, nop, stz, sta, stz, bbs1, /* 9 */ +/* A */ ldy, lda, ldx, nop, ldy, lda, ldx, smb2, tay, lda, tax, nop, ldy, lda, ldx, bbs2, /* A */ +/* B */ bcs, lda, lda, nop, ldy, lda, ldx, smb3, clv, lda, tsx, nop, ldy, lda, ldx, bbs3, /* B */ +/* C */ cpy, cmp, nop, nop, cpy, cmp, dec, smb4, iny, cmp, dex, wai, cpy, cmp, dec, bbs4, /* C */ +/* D */ bne, cmp, cmp, nop, nop, cmp, dec, smb5, cld, cmp, phx, dbg, nop, cmp, dec, bbs5, /* D */ +/* E */ cpx, sbc, nop, nop, cpx, sbc, inc, smb6, inx, sbc, nop, nop, cpx, sbc, inc, bbs6, /* E */ +/* F */ beq, sbc, sbc, nop, nop, sbc, inc, smb7, sed, sbc, plx, nop, nop, sbc, inc, bbs7 /* F */ +}; + +static void (*optable_c816[256])() = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ brk, ora, cop, ora, tsb, ora, asl, ora, php, ora, asl, phd, tsb, ora, asl, ora, /* 0 */ +/* 1 */ bpl, ora, ora, ora, trb, ora, asl, ora, clc, ora, inc, tcs, trb, ora, asl, ora, /* 1 */ +/* 2 */ jsr, and, jsl, and, bit, and, rol, and, plp, and, rol, pld, bit, and, rol, and, /* 2 */ +/* 3 */ bmi, and, and, and, bit, and, rol, and, sec, and, dec, tsc, bit, and, rol, and, /* 3 */ +/* 4 */ rti, eor, wdm, eor, mvp, eor, lsr, eor, pha, eor, lsr, phk, jmp, eor, lsr, eor, /* 4 */ +/* 5 */ bvc, eor, eor, eor, mvn, eor, lsr, eor, cli, eor, phy, tcd, jml, eor, lsr, eor, /* 5 */ +/* 6 */ rts, adc, per, adc, stz, adc, ror, adc, pla, adc, ror, rtl, jmp, adc, ror, adc, /* 6 */ +/* 7 */ bvs, adc, adc, adc, stz, adc, ror, adc, sei, adc, ply, tdc, jmp, adc, ror, adc, /* 7 */ +/* 8 */ bra, sta, brl, sta, sty, sta, stx, sta, dey, bit, txa, phb, sty, sta, stx, sta, /* 8 */ +/* 9 */ bcc, sta, sta, sta, sty, sta, stx, sta, tya, sta, txs, txy, stz, sta, stz, sta, /* 9 */ +/* A */ ldy, lda, ldx, lda, ldy, lda, ldx, lda, tay, lda, tax, plb, ldy, lda, ldx, lda, /* A */ +/* B */ bcs, lda, lda, lda, ldy, lda, ldx, lda, clv, lda, tsx, tyx, ldy, lda, ldx, lda, /* B */ +/* C */ cpy, cmp, rep, cmp, cpy, cmp, dec, cmp, iny, cmp, dex, wai, cpy, cmp, dec, cmp, /* C */ +/* D */ bne, cmp, cmp, cmp, pei, cmp, dec, cmp, cld, cmp, phx, dbg, jml, cmp, dec, cmp, /* D */ +/* E */ cpx, sbc, sep, sbc, cpx, sbc, inc, sbc, inx, sbc, nop, xba, cpx, sbc, inc, sbc, /* E */ +/* F */ beq, sbc, sbc, sbc, pea, sbc, inc, sbc, sed, sbc, plx, xce, jsr, sbc, inc, sbc /* F */ +}; + +static const uint32_t ticktable_c02[256] = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ 7, 6, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 5, /* 0 */ +/* 1 */ 2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 7, 5, /* 1 */ +/* 2 */ 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 5, /* 2 */ +/* 3 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 7, 5, /* 3 */ +/* 4 */ 6, 6, 2, 1, 3, 3, 5, 5, 3, 2, 2, 1, 3, 4, 6, 5, /* 4 */ +/* 5 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 7, 5, /* 5 */ +/* 6 */ 6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 5, 4, 6, 5, /* 6 */ +/* 7 */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 6, 4, 7, 5, /* 7 */ +/* 8 */ 3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, /* 8 */ +/* 9 */ 2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 5, /* 9 */ +/* A */ 2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, /* A */ +/* B */ 2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 5, /* B */ +/* C */ 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, /* C */ +/* D */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 4, 4, 7, 5, /* D */ +/* E */ 2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 5, /* E */ +/* F */ 2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 7, 5 /* F */ +}; + +static const uint32_t ticktable_c816[256] = { +/* | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | */ +/* 0 */ 7, 6, 7, 4, 5, 3, 5, 6, 3, 2, 2, 4, 6, 4, 6, 5, /* 0 */ +/* 1 */ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 2, 2, 6, 4, 7, 5, /* 1 */ +/* 2 */ 6, 6, 8, 4, 3, 3, 5, 6, 4, 2, 2, 5, 4, 4, 6, 5, /* 2 */ +/* 3 */ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 2, 2, 4, 4, 7, 5, /* 3 */ +/* 4 */ 6, 6, 2, 4, 7, 3, 5, 6, 3, 2, 2, 3, 3, 4, 6, 5, /* 4 */ +/* 5 */ 2, 5, 5, 7, 7, 4, 6, 6, 2, 4, 3, 2, 4, 4, 7, 5, /* 5 */ +/* 6 */ 6, 6, 6, 4, 3, 3, 5, 6, 4, 2, 2, 6, 5, 4, 6, 5, /* 6 */ +/* 7 */ 2, 5, 5, 7, 4, 4, 6, 6, 2, 4, 4, 2, 6, 4, 7, 5, /* 7 */ +/* 8 */ 3, 6, 4, 4, 3, 3, 3, 6, 2, 2, 2, 3, 4, 4, 4, 5, /* 8 */ +/* 9 */ 2, 6, 5, 7, 4, 4, 4, 6, 2, 5, 2, 2, 4, 5, 5, 5, /* 9 */ +/* A */ 2, 6, 2, 4, 3, 3, 3, 6, 2, 2, 2, 4, 4, 4, 4, 5, /* A */ +/* B */ 2, 5, 5, 7, 4, 4, 4, 6, 2, 4, 2, 2, 4, 4, 4, 5, /* B */ +/* C */ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, /* C */ +/* D */ 2, 5, 5, 7, 6, 4, 6, 6, 2, 4, 3, 1, 6, 4, 7, 5, /* D */ +/* E */ 2, 6, 3, 4, 3, 3, 5, 6, 2, 2, 2, 3, 4, 4, 6, 5, /* E */ +/* F */ 2, 5, 5, 7, 5, 4, 6, 6, 2, 4, 4, 2, 8, 4, 7, 5 /* F */ +}; From e4cc029948e7cee31ab3c6ac5799675e2c37d536 Mon Sep 17 00:00:00 2001 From: morphinejh <78104266+morphinejh@users.noreply.github.com> Date: Tue, 12 May 2026 21:17:52 -0400 Subject: [PATCH 5/7] better status information --- src/extern/serialuart/serialuartTL16C2550.cpp | 2 +- src/main.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/extern/serialuart/serialuartTL16C2550.cpp b/src/extern/serialuart/serialuartTL16C2550.cpp index e106f5ab..a5110554 100644 --- a/src/extern/serialuart/serialuartTL16C2550.cpp +++ b/src/extern/serialuart/serialuartTL16C2550.cpp @@ -944,7 +944,7 @@ int serialuartTL16C2550::init(char *port){ #endif path += (char *)port; - std::cout << "Attempting: " << path << ", "; + std::cout << "Trying port: " << path << " - "; int errorOpening = this->serialPort.openDevice(path.c_str(), 9600); if (errorOpening != 1) { diff --git a/src/main.c b/src/main.c index e0c74588..9c522e31 100644 --- a/src/main.c +++ b/src/main.c @@ -363,6 +363,9 @@ machine_reset() if (uart_init( uart1, (char*)uart1_path ) ){ has_uart1=false; } + else{ + printf("UART1 succesuflly mapped to address: $%04x\n", uart1_addr); + } } else { has_uart1=false; } @@ -377,6 +380,9 @@ machine_reset() if (uart_init( uart2, (char*)uart2_path ) ){ has_uart2=false; } + else{ + printf("UART2 succesuflly mapped to address: $%04x\n", uart2_addr); + } } else { has_uart2=false; } @@ -1290,10 +1296,17 @@ main(int argc, char **argv) } if (has_uart2 || has_uart1) { - if (uart1_addr < 0x9f60 ) { - fprintf(stderr, "Warning: Serial UART card address must be in the range of 9F60-9FF0, Unabled to connect\n"); + //Techincally cannot be less than 0x9f0 due to above code in argument parsing. + if (uart1_addr < 0x9f60 || uart1_addr>0x9ff8) { + fprintf(stderr, "Warning: Serial UART card address must be in the range of 9F60-9FF8, Unabled to connect\n"); has_uart1 = has_uart2 = false; } + + //Make sure to stay in memory mapped range. + if(has_uart2 && uart1_addr>0x9ff0){ + has_uart2=false; + fprintf(stderr, "Warning: Serial UART1 card address must be in the range of 9F60-9FF0 when using two UARTs, Unabled to connect\n"); + } } if (cartridge_path) { From 506654b2248e5e25fbda7b6e9a34405446483202 Mon Sep 17 00:00:00 2001 From: morphinejh <78104266+morphinejh@users.noreply.github.com> Date: Tue, 12 May 2026 21:23:31 -0400 Subject: [PATCH 6/7] better status information --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 9c522e31..2433f56f 100644 --- a/src/main.c +++ b/src/main.c @@ -1220,8 +1220,8 @@ main(int argc, char **argv) } if (argc && argv[0][0] != '-') { + //Force to memory mapped addresses only uart1_addr = 0x9f00 | ((uint16_t)strtol(argv[0], NULL, 16) & 0xff); - //uart1_addr &= 0xfff0; argc--; argv++; } else { From 2a75b39e52fbcfbb209c1a21cd28f8866b83a8d9 Mon Sep 17 00:00:00 2001 From: morphinejh <78104266+morphinejh@users.noreply.github.com> Date: Wed, 27 May 2026 23:43:12 -0400 Subject: [PATCH 7/7] report AFE bit correctly --- src/extern/serialuart/serialuartTL16C2550.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extern/serialuart/serialuartTL16C2550.cpp b/src/extern/serialuart/serialuartTL16C2550.cpp index a5110554..7023a67c 100644 --- a/src/extern/serialuart/serialuartTL16C2550.cpp +++ b/src/extern/serialuart/serialuartTL16C2550.cpp @@ -749,7 +749,7 @@ int serialuartTL16C2550::addrread(unsigned char *value, int address){ // Offset 4 – Modem Control Register // ------------------------------------------------------------------- case 4: - *value = MCR & 0x1F; // bits 7:5 are not implemented + *value = MCR & 0x3F; // bits 7:5 are not implemented retVal = 1; break;