WIP 40x4 LCD: support in bios and emulator, minimal test rom, schematics

This commit is contained in:
Daniele Verducci 2025-02-26 08:42:33 +01:00
parent bf91ff680d
commit f7afa69f66
9 changed files with 4934 additions and 39 deletions

View File

@ -0,0 +1,102 @@
; TM404A 40x4 characters LCD display (based on SN76489 chip) driver
; @author Daniele Verducci
; @language: Z80 ASM
;
;
; This file is part of Pat80 Memory Monitor.
;
; Pat80 Memory Monitor is free software: you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation, either version 3 of the License, or
; (at your option) any later version.
;
; Pat80 Memory Monitor is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with Pat80 Memory Monitor. If not, see <http://www.gnu.org/licenses/>.
;
; --------------------------------------------------------------------------
;
; This is meant to be used with a 40x4 character display (containing a dual hd44780-compatible controller) like the TM404A.
; This kind of display uses two controllers to overcome the hd44780 controllers limitation of 80 addressable characters.
; It is seen as two separate displays, and thus have two EN pins. A finer implementation would apply some deconding logic to
; an address bit to use a single port, but here we will use two different IO ports to save an IC.
;
; LCD config (IO port 0)
LCD4004_TOP2LINES_PORT: EQU IO_2
LCD4004_BOTTOM2LINES_PORT: EQU IO_3
; PIN CONNECTIONS
;
; PIN DESCRIPTION TO PAT80 BUS PIN INFO
; ---------------------------------------------------------------------------------------------------
; 18 DB7DB0 DATA BUS
; 9 E1 (chip en 1) IOEN on LCD_TOP2LINES_PORT Chip enable for top 2 lines
; 10 R/W IOWR
; 11 RS A4 0: Command, 1: Data
; 12 V0 - Power supply for contrast (approx. +0.5V)
; 13 Vss - Ground
; 14 VDD - Power Supply Supply voltage for logic (+5.0V)
; 15 E2 (chip en 2) IOEN on LCD_BOTTOM2LINES_PORT Chip enable for bottom 2 lines
; 16 NC No Connect
;
; The 40x4 LCD memory map is the following:
;
; LINE CHIP EN PIN START ADDR END ADDR
; ----------------------------------------------------
; 1 9 0x00 (0) 0x27 (39)
; 2 9 0x40 (64) 0x67 (103)
; 3 15 0x00 (0) 0x27 (39)
; 4 15 0x40 (64) 0x67 (103)
;
; When we reach 0x40 on the first controller, we switch to the second.
; When we reach 0x40 on the second controller, we must shift
; all the lines up by one and position the cursor at 0x40.
; variables
LCD4004_VAR_SPACE: EQU DRV_IO_2_VAR_SPACE
LCD4004_CURSOR_POSITION_CONTROLLER: EQU DRV_IO_2_VAR_SPACE ; In which LCD controller is the cursor (0 or 1)
LCD4004_CURSOR_POSITION_CONTROLLER_MEMORY: EQU DRV_IO_2_VAR_SPACE + 1 ; Memory position of the cursor inside the controller's memory
; functions
; Initializes the driver and the LCD screen
LCD4004_Initialize:
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
; Clear display
; 0000000001
; Move cursor (set DDRAM address in address counter)
LCD4004_MoveCursor:
; 0 0 1 AC6 AC5 AC4 AC3 AC2 AC1 AC0 (ACx: Address)
; Sends string
; @param BC Pointer to a null-terminated string first character
LCD4004_print:
ld a, (bc) ; bc is the pointer to passed string's first char
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
ret z ; if prev compare is true (Z flag set), string is finished, return
out (TERM_DATA_REG),a ; output char
inc bc ; increment bc to move to next char
jp LCD4004_print
; Writes a single character
; @param A Value of character to print
LCD4004_printc:
out (TERM_DATA_REG),a
ret

View File

@ -23,7 +23,7 @@ TERM_DATA_REG: EQU IO_0
TERM_DATA_AVAIL_REG: EQU IO_0 + 1 TERM_DATA_AVAIL_REG: EQU IO_0 + 1
; variables ; variables
TERM_VAR_SPACE: EQU DRV_VAR_SPACE + 128 TERM_VAR_SPACE: EQU DRV_IO_0_VAR_SPACE
incoming_string: EQU TERM_VAR_SPACE incoming_string: EQU TERM_VAR_SPACE
; functions ; functions

View File

@ -30,8 +30,8 @@ jp Sysinit ; Startup vector: DO NOT MOVE! Must be the first instruction
; I/O MAP ; I/O MAP
; I/O 0 (0x00 - 0x1F) Parallel terminal (uses addr 0x00 and 0x01) ; I/O 0 (0x00 - 0x1F) Parallel terminal (uses addr 0x00 and 0x01)
; I/O 1 (0x20 - 0x3F) Sound card (uses addr 0x20 only) ; I/O 1 (0x20 - 0x3F) Sound card (uses addr 0x20 only)
; I/O 2 (0x40 - 0x5F) PS2 Keyboard (uses 0x40 and 0x41) ; I/O 2 (0x40 - 0x5F) 40x4 LCD, first controller (lines 1 and 2)
; I/O 3 (0x60 - 0x7F) ; I/O 3 (0x60 - 0x7F) 40x4 LCD, second controller (lines 3 and 4)
; I/O 4 (0x80 - 0x9F) ; I/O 4 (0x80 - 0x9F)
; I/O 5 (0xA0 - 0xBF) ; I/O 5 (0xA0 - 0xBF)
; I/O 6 (0xC0 - 0xDF) ; I/O 6 (0xC0 - 0xDF)
@ -89,12 +89,20 @@ Sys_Beep:
; MEMORY CONFIGURATION ; MEMORY CONFIGURATION
SYS_VAR_SPACE: EQU 0x8000 SYS_VAR_SPACE: EQU 0x8000 ; OS may allocate here memory for its own use
DRV_VAR_SPACE: EQU 0x9000 DRV_VAR_SPACE: EQU 0x9000 ; IO devices drivers may allocate here memory, each has a chunk of 512 bytes
APP_SPACE: EQU 0xA000 DRV_IO_0_VAR_SPACE: EQU DRV_VAR_SPACE
DRV_IO_1_VAR_SPACE: EQU DRV_IO_0_VAR_SPACE + 512
DRV_IO_2_VAR_SPACE: EQU DRV_IO_1_VAR_SPACE + 512
DRV_IO_3_VAR_SPACE: EQU DRV_IO_2_VAR_SPACE + 512
DRV_IO_4_VAR_SPACE: EQU DRV_IO_3_VAR_SPACE + 512
DRV_IO_5_VAR_SPACE: EQU DRV_IO_4_VAR_SPACE + 512
DRV_IO_6_VAR_SPACE: EQU DRV_IO_5_VAR_SPACE + 512
DRV_IO_7_VAR_SPACE: EQU DRV_IO_6_VAR_SPACE + 512
APP_SPACE: EQU 0xA000 ; App may only allocate between there and MEM_END
MEM_END: EQU 0xFFFF MEM_END: EQU 0xFFFF
; SYSTEM CONFIGURATION ; DEVICES ("cards" on the IO bus) I/O space
IO_0: EQU 0x00 IO_0: EQU 0x00
IO_1: EQU 0x20 IO_1: EQU 0x20
IO_2: EQU 0x40 IO_2: EQU 0x40

View File

@ -0,0 +1,116 @@
jp main ; Startup vector: DO NOT MOVE! Must be the first instruction
; Tests TM404A, on ports 2 and 3
IO_2: EQU 0x40
IO_3: EQU 0x60
LCD_TOP_INSTR_REG: EQU IO_2
LCD_TOP_DATA_REG: EQU IO_2 + 1
LCD_BOTTOM_INSTR_REG: EQU IO_3
LCD_BOTTOM_DATA_REG: EQU IO_3 + 1
; Inits the lcd display
LCD4004_init:
; --- Init first controller ---
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
ld a,0x38
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
ld a,0x0F
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; Clear display
; 0000000001
ld a,0x01
out (LCD_TOP_INSTR_REG),a
call LCD4004_wait_busy_clear
; --- Init second controller ---
; The following are documented as in the datasheet: RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
; Function set: 0 0 0 0 1 DL N F 0 0 (DL: 8-bit/4-bit, N: number of display lines 2/1, F: Font type 11dots/8dots)
; 0000111000 (8 bit, 2 lines)
ld a,0x38
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
; Display ON/OFF Control: 0 0 0 0 0 0 1 D C B (D: display on/off, C: cursor on/off, B: blinking cursor on/off)
; 0000001111 (display on, blinking cursor)
ld a,0x0F
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
; Clear display
; 0000000001
ld a,0x01
out (LCD_BOTTOM_INSTR_REG),a
call LCD4004_wait_busy_clear
ret
; Prints string
; @param BC Pointer to a null-terminated string first character
LCD4004_TOP_print:
ld a, (bc) ; bc is the pointer to passed string's first char
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
ret z ; if prev compare is true (Z flag set), string is finished, return
out (LCD_TOP_DATA_REG),a ; output char
call LCD4004_wait_busy_clear ; wait for the lcd to execute (busy signal check)
inc bc ; increment bc to move to next char
jp LCD4004_TOP_print
; Prints string
; @param BC Pointer to a null-terminated string first character
LCD4004_BOTTOM_print:
ld a, (bc) ; bc is the pointer to passed string's first char
cp 0 ; compare A content with 0 (subtract 0 from value and set zero flag Z if result is 0)
ret z ; if prev compare is true (Z flag set), string is finished, return
out (LCD_BOTTOM_DATA_REG),a ; output char
call LCD4004_wait_busy_clear ; wait for the lcd to execute (busy signal check)
inc bc ; increment bc to move to next char
jp LCD4004_BOTTOM_print
; Waits for the busy flag to be clear, indicating the LCD controllers to have finished their work
LCD4004_wait_busy_clear:
ret
in a, (LCD_TOP_INSTR_REG) ; reads the status
rla ; busy flag is DB7, so we shift it into carry to check it
jp c, LCD4004_wait_busy_clear ; if carry is set, lcd is busy
in a, (LCD_BOTTOM_INSTR_REG) ; reads the status
rla ; busy flag is DB7, so we shift it into carry to check it
jp c, LCD4004_wait_busy_clear ; if carry is set, lcd is busy
ret
; --- TEST CODE ---
main:
TEST_STR_80: DB "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam augue tortor sed.",0 ; null terminated string
TEST_STR_85: DB "2Lorem ipsum dolor sit amet, consectetur adipiscing elit praesent pellentesque nisi.",0 ; null terminated string
; Init display
call LCD4004_init
; Write string on first 2 lines
ld bc, TEST_STR_80
call LCD4004_TOP_print
; Write long string on last 2 lines (to see how it wraps)
ld bc, TEST_STR_85
call LCD4004_BOTTOM_print
halt

View File

@ -2,6 +2,7 @@
* PAT80 Emulator * PAT80 Emulator
* *
* Emulates a PAT80. * Emulates a PAT80.
* Based on https://github.com/redcode/Z80
*/ */
#include <Z/constants/pointer.h> /* Z_NULL */ #include <Z/constants/pointer.h> /* Z_NULL */
@ -29,7 +30,8 @@ typedef struct {
Z80 cpu; Z80 cpu;
WINDOW *terminal_win; WINDOW *terminal_win;
WINDOW *status_win; WINDOW *status_win;
WINDOW *lcd_win; WINDOW *lcd_top_win; // 40x4 LCD, top two lines
WINDOW *lcd_bottom_win; // 40x4 LCD, bottom two lines
} Machine; } Machine;
@ -50,8 +52,8 @@ static zuint8 machine_cpu_in(Machine *self, zuint16 port) {
// so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits // so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits
// A4-A0 may be used by the single device, at its own discretion. // A4-A0 may be used by the single device, at its own discretion.
zuint16 bitmask = 7; // 0000000000000111 zuint16 bitmask = 7; // 0000000000000111
int decoded = port & bitmask; int ioDevice = port & bitmask;
if (decoded <= 0x1F) { if (ioDevice <= 0x1F) {
// Port 0 (0x00 to 0x1F): terminal // Port 0 (0x00 to 0x1F): terminal
// Read char from stin // Read char from stin
char c = getch(); char c = getch();
@ -66,37 +68,45 @@ static zuint8 machine_cpu_in(Machine *self, zuint16 port) {
return c; return c;
} }
} }
if (decoded <= 0x3F) { if (ioDevice <= 0x3F) {
// Port 1 (0x20 to 0x3F): sound card (sn76489) // Port 1 (0x20 to 0x3F): sound card (sn76489)
wprintw(self->status_win, "sound_cmd[IN]: Not supported!\n"); wprintw(self->status_win, "sound_cmd[IN]: Not supported!\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x5F) { if (ioDevice <= 0x5F) {
// Port 2 (0x40 to 0x5F) // Port 2 (0x40 to 0x5F): LCD top 2 lines
if (port == 0x40 || port == 0x60) {
// TODO: Simulate busy flag and cursor position
return 0x00; // Busy flag clear, for now
}
wprintw(self->status_win, "IO_ERROR_IN: No device at port 2\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 2\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x7F) { if (ioDevice <= 0x7F) {
// Port 3 (0x60 to 0x7F) // Port 3 (0x60 to 0x7F): LCD bottom 2 lines
if (port == 0x0) {
// TODO: Simulate busy flag and cursor position
return 0x00;
}
wprintw(self->status_win, "IO_ERROR_IN: No device at port 3\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 3\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x9F) { if (ioDevice <= 0x9F) {
// Port 4 (0x80 to 0x9F) // Port 4 (0x80 to 0x9F)
wprintw(self->status_win, "IO_ERROR_IN: No device at port 4\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 4\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x5F) { if (ioDevice <= 0x5F) {
// Port 5 (0xA0 to 0xBF) // Port 5 (0xA0 to 0xBF)
wprintw(self->status_win, "IO_ERROR_IN: No device at port 5\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 5\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x5F) { if (ioDevice <= 0x5F) {
// Port 6 (0xC0 to 0xDF) // Port 6 (0xC0 to 0xDF)
wprintw(self->status_win, "IO_ERROR_IN: No device at port 6\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 6\n");
return 0x00; return 0x00;
} }
if (decoded <= 0x5F) { if (ioDevice <= 0x5F) {
// Port 7 (0xE0 to 0xFF) // Port 7 (0xE0 to 0xFF)
wprintw(self->status_win, "IO_ERROR_IN: No device at port 7\n"); wprintw(self->status_win, "IO_ERROR_IN: No device at port 7\n");
} else { } else {
@ -107,7 +117,8 @@ static zuint8 machine_cpu_in(Machine *self, zuint16 port) {
refresh(); refresh();
wrefresh(self->terminal_win); wrefresh(self->terminal_win);
wrefresh(self->status_win); wrefresh(self->status_win);
wrefresh(self->lcd_win); wrefresh(self->lcd_top_win);
wrefresh(self->lcd_bottom_win);
} }
@ -117,29 +128,56 @@ static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value) {
// so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits // so the 3 most significant IO addr bits in this case are A7, A6, A5. The bits
// A4-A0 may be used by the single device, at its own discretion. // A4-A0 may be used by the single device, at its own discretion.
zuint16 bitmask = 0xE0; // 0000000011100000 zuint16 bitmask = 0xE0; // 0000000011100000
int decoded = port & bitmask; int ioDevice = port & bitmask;
if (decoded <= 0x1F) { bitmask = 0x1F; // 0000000000011111
int ioAddrInsideDevice = port & bitmask;
wprintw(self->status_win, "[%#06x]DECODED[%#04x] - ", port, ioDevice);
if (ioDevice <= 0x1F) {
// Port 0 (0x00 to 0x1F): terminal // Port 0 (0x00 to 0x1F): terminal
wprintw(self->terminal_win, "%c", value); wprintw(self->terminal_win, "%c", value);
} else if (decoded <= 0x3F) { } else if (ioDevice <= 0x3F) {
// Port 1 (0x20 to 0x3F): sound card (sn76489) // Port 1 (0x20 to 0x3F): sound card (sn76489)
wprintw(self->status_win, "sound_cmd[%#04x]\n", value); wprintw(self->status_win, "sound_cmd[%#04x]\n", value);
} else if (decoded <= 0x5F) { } else if (ioDevice <= 0x5F) {
// Port 2 (0x40 to 0x5F): lcd display 40x4 (mod. TM404A, based on 2 KS0066 chips, each one controlling 2 rows) // Port 2 (0x40 to 0x5F) and Port 3 (0x60 to 0x7F):
wprintw(self->lcd_win, "%c", value); // lcd display 40x4 (mod. TM404A, based on 2 KS0066 chips, each one controlling 2 rows,
} else if (decoded <= 0x7F) { // top controller at port 2 and bottom at port 3).
// Port 3 (0x60 to 0x7F) // Instruction register at each port first address, data register at second address
wprintw(self->status_win, "IO_ERROR_OUT: No device at port 3\n"); if (ioAddrInsideDevice == 0) {
} else if (decoded <= 0x9F) { // Port 2, A4 LOW = talking to LCD top 2 lines instruction register
wprintw(self->status_win, "lcd_top_cmd[%#04x]\n", value);
} else if (ioAddrInsideDevice == 1) {
// Port 2, A4 HIGH = talking to LCD top 2 lines data register (writing text to screen)
wprintw(self->status_win, "lcd_top_data[%#04x](%c)\n", value, value);
wprintw(self->lcd_top_win, "%c", value);
} else {
wprintw(self->status_win, "IO_ERROR_OUT: lcd (top controller) does not listen at addr %#04x\n", ioAddrInsideDevice);
}
} else if (ioDevice <= 0x7F) {
// Port 2 (0x40 to 0x5F) and Port 3 (0x60 to 0x7F):
// lcd display 40x4 (mod. TM404A, based on 2 KS0066 chips, each one controlling 2 rows,
// top controller at port 2 and bottom at port 3).
// Instruction register at each port first address, data register at second address
if (ioAddrInsideDevice == 0) {
// Port 3, A4 LOW = talking to LCD bottom 2 lines instruction register
wprintw(self->status_win, "lcd_bottom_cmd[%#04x]\n", value);
} else if (ioAddrInsideDevice == 1) {
// Port 3, A4 HIGH = talking to LCD bottom 2 lines data register (writing text to screen)
wprintw(self->status_win, "lcd_bottom_data[%#04x](%c)\n", value, value);
wprintw(self->lcd_bottom_win, "%c", value);
} else {
wprintw(self->status_win, "IO_ERROR_OUT: lcd (bottom controller) does not listen at addr %#04x\n", ioAddrInsideDevice);
}
} else if (ioDevice <= 0x9F) {
// Port 4 (0x80 to 0x9F) // Port 4 (0x80 to 0x9F)
wprintw(self->status_win, "IO_ERROR_OUT: No device at port 4\n"); wprintw(self->status_win, "IO_ERROR_OUT: No device at port 4\n");
} else if (decoded <= 0x5F) { } else if (ioDevice <= 0xBF) {
// Port 5 (0xA0 to 0xBF) // Port 5 (0xA0 to 0xBF)
wprintw(self->status_win, "IO_ERROR_OUT: No device at port 5\n"); wprintw(self->status_win, "IO_ERROR_OUT: No device at port 5\n");
} else if (decoded <= 0x5F) { } else if (ioDevice <= 0xDF) {
// Port 6 (0xC0 to 0xDF) // Port 6 (0xC0 to 0xDF)
wprintw(self->status_win, "IO_ERROR_OUT: No device at port 6\n"); wprintw(self->status_win, "IO_ERROR_OUT: No device at port 6\n");
} else if (decoded <= 0x5F) { } else if (ioDevice <= 0xFF) {
// Port 7 (0xE0 to 0xFF) // Port 7 (0xE0 to 0xFF)
wprintw(self->status_win, "IO_ERROR_OUT: No device at port 7\n"); wprintw(self->status_win, "IO_ERROR_OUT: No device at port 7\n");
} else { } else {
@ -150,7 +188,21 @@ static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value) {
refresh(); refresh();
wrefresh(self->terminal_win); wrefresh(self->terminal_win);
wrefresh(self->status_win); wrefresh(self->status_win);
wrefresh(self->lcd_win); wrefresh(self->lcd_top_win);
wrefresh(self->lcd_bottom_win);
}
static void machine_cpu_halt(Machine *self, unsigned char signal) {
wprintw(self->status_win, "HALTED (%d)\n", signal);
// Refresh all windows
refresh();
wrefresh(self->terminal_win);
wrefresh(self->status_win);
wrefresh(self->lcd_top_win);
wrefresh(self->lcd_bottom_win);
// Wait an ESC before exiting
while (getch() != 27) {}
exit(0);
} }
@ -163,7 +215,7 @@ void machine_initialize(Machine *self) {
self->cpu.write = (Z80Write)machine_cpu_write; self->cpu.write = (Z80Write)machine_cpu_write;
self->cpu.in = (Z80Read )machine_cpu_in; self->cpu.in = (Z80Read )machine_cpu_in;
self->cpu.out = (Z80Write)machine_cpu_out; self->cpu.out = (Z80Write)machine_cpu_out;
self->cpu.halt = Z_NULL; self->cpu.halt = (Z80Halt)machine_cpu_halt;
self->cpu.nmia = Z_NULL; self->cpu.nmia = Z_NULL;
self->cpu.inta = Z_NULL; self->cpu.inta = Z_NULL;
self->cpu.int_fetch = Z_NULL; self->cpu.int_fetch = Z_NULL;
@ -224,15 +276,18 @@ int main(int argc, char *argv[]) {
/*Z80*/ .cpu = pat80Cpu, /*Z80*/ .cpu = pat80Cpu,
.terminal_win = newwin(TERMINAL_HEIGHT, TERMINAL_WIDTH, INSTRUCTION_WINDOW_HEIGHT, 0), .terminal_win = newwin(TERMINAL_HEIGHT, TERMINAL_WIDTH, INSTRUCTION_WINDOW_HEIGHT, 0),
.status_win = newwin(y, x - TERMINAL_WIDTH - SPACING_BETWEEN_WINDOWS, INSTRUCTION_WINDOW_HEIGHT, TERMINAL_WIDTH + SPACING_BETWEEN_WINDOWS), // To right of terminal window .status_win = newwin(y, x - TERMINAL_WIDTH - SPACING_BETWEEN_WINDOWS, INSTRUCTION_WINDOW_HEIGHT, TERMINAL_WIDTH + SPACING_BETWEEN_WINDOWS), // To right of terminal window
.lcd_win = newwin(4, 40, INSTRUCTION_WINDOW_HEIGHT + TERMINAL_HEIGHT + SPACING_BETWEEN_WINDOWS, 0) // Below terminal window .lcd_top_win = newwin(2, 40, INSTRUCTION_WINDOW_HEIGHT + TERMINAL_HEIGHT + SPACING_BETWEEN_WINDOWS, 0), // Below terminal window
.lcd_bottom_win = newwin(2, 40, INSTRUCTION_WINDOW_HEIGHT + TERMINAL_HEIGHT + SPACING_BETWEEN_WINDOWS + 2, 0)
}; };
wbkgd(pat80.terminal_win, COLOR_PAIR(1)); // Ncurses: set terminal window color wbkgd(pat80.terminal_win, COLOR_PAIR(1)); // Ncurses: set terminal window color
wbkgd(pat80.status_win, COLOR_PAIR(2)); wbkgd(pat80.status_win, COLOR_PAIR(2));
wbkgd(pat80.lcd_win, COLOR_PAIR(3)); wbkgd(pat80.lcd_top_win, COLOR_PAIR(3));
wbkgd(pat80.lcd_bottom_win, COLOR_PAIR(3));
scrollok(pat80.terminal_win, TRUE); // Ncurses: Allow scrolling when reached end of window scrollok(pat80.terminal_win, TRUE); // Ncurses: Allow scrolling when reached end of window
scrollok(pat80.status_win, TRUE); scrollok(pat80.status_win, TRUE);
scrollok(pat80.lcd_win, FALSE); scrollok(pat80.lcd_top_win, FALSE);
scrollok(pat80.lcd_bottom_win, FALSE);
attron(A_BOLD); // Print instructions attron(A_BOLD); // Print instructions
printw("Emulator commands\n"); printw("Emulator commands\n");
attroff(A_BOLD); attroff(A_BOLD);
@ -258,7 +313,8 @@ int main(int argc, char *argv[]) {
// Stop ncurses // Stop ncurses
delwin(pat80.terminal_win); delwin(pat80.terminal_win);
delwin(pat80.status_win); delwin(pat80.status_win);
delwin(pat80.lcd_win); delwin(pat80.lcd_top_win);
delwin(pat80.lcd_bottom_win);
endwin(); endwin();
return 0; return 0;
} }

View File

@ -0,0 +1,2 @@
(kicad_pcb (version 20240108) (generator "pcbnew") (generator_version "8.0")
)

View File

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 0,
"active_layer_preset": "",
"auto_track_width": true,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
0,
1,
2,
3,
4,
5,
8,
9,
10,
11,
12,
13,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36,
39,
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "4004_lcd_display.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View File

@ -0,0 +1,392 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"rules": {},
"track_widths": [],
"via_dimensions": []
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "ignore",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "4004_lcd_display.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Riferimento"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"b60673c4-5448-4b81-8c25-50d47b52f4a5",
"Root"
]
],
"text_variables": {}
}