Tag Archives: retro

C64 PRG to cartridge.

I’ve got the tools and Bigred made me enthusiastic again.
My goal is to make a C64 Cartridge from a PRG. And Not any program, it is the 8085 Emulator from Sepp.

Serveral problems i have to ‘fix’

  • The program is 17K, Cartridges can only be 16K.
    So i have to use 2x 8K and compress the data.
    This means it have to be uncompressed at start time.
    ( I was thinking of using exomiser for this )
  • Program starts normally at $0820 and probably is not optimised to run anywhere else.
    So a starting routine has to copy the program from cartridge memory to the correct location

Luckily i have the source! How cool is that

For version 4.73 it states : Starting at $0820 .. but my hexdump is off by one??!?

root@battlestation:/home/fash/Projects/minipro# hexdump -C /tmp/8085.prg  | head
00000000  01 08 1e 08 c5 07 9e 32  30 38 30 20 42 59 20 4d  |.......2080 BY M|
00000010  41 52 54 49 4e 20 4d 45  59 45 52 49 4e 4b 00 00  |ARTIN MEYERINK..|
00000020  00 20 ec 08 20 7f 19 20  2b 2c 20 11 19 20 b8 08  |. .. .. +, .. ..|
00000030  20 20 2c 20 a0 2c 20 f2  2c 20 11 e1 4c 00 15 aa  |  , ., ., ..L...|
00000040  aa a2 06 ad b7 08 9d 48  d8 bd 48 04 20 88 39 9d  |.......H..H. .9.|
00000050  48 04 ca 10 ee a9 60 8d  4c 04 4c 50 47 00 a9 d0  |H.....`.L.LPG...|
00000060  2c a9 f0 8d 45 1f 4c 11  e1 1e 93 0d 20 20 4d 41  |,...E.L.....  MA|
00000070  52 54 49 4e 20 4d 45 59  45 52 49 4e 4b 27 53 0d  |RTIN MEYERINK'S.|
00000080  0d 20 38 30 38 35 20 45  4d 55 4c 41 54 4f 52 20  |. 8085 EMULATOR |
00000090  20 56 34 2e 38 30 0d 0d  20 20 28 43 29 20 31 20  | V4.80..  (C) 1 |

00000020 00 20 ec starts with 00 at $0020 .. and not 20 ?!?!

Tools used until now:

  • Vice – C64 Emulator
    x64 -cartcrt 8085.crt
  • c1541 – Linux disk tool for C64 images.
    Used this to extract the 8085emulator PRG
  • prg2crt.py – a convertor from PRG to a cartrid file which can be used by Vice
    python2 prg2crt.py 8085.prg 8085.crt
  • minipro – eeprom programming tool for Linux
    minipro -p AT28C64 -w /tmp/test.bin
  • cartconv (tool from vice to convert crt <-> bin)
    cartconv -t normal -i test.bin -n ‘my cart’ -o test.crt
  • xa – Cross assembler 65xx/R65C02/65816
  • ACME – the ACME Crossassembler for Multiple Environments
Memory Map C64 – source c64-wiki.com

Card Low starts at $8000, so that’s the place where those roms are going to be.
To place on this address:

Copy routine : from ($8000 + this copy routine) to $0820
When to decompress??
jmp routine to $0820

A cartridge file >16K and with his emulation headers seems to work??!

Also nice: Magic Desk Cartridge Generator V3.0

UPDATE: 20220811

exomizer sfx 0x0820 8085.prg -o data.exo # Compress and start at 0x0820 
xa frame.asm -o frame.bin # Add code and write binary
x64 --cart16 frame.bin # Test cartridge with Vice

frame.asm

;---------------------------------------------------------- 
; example usage
; xa frame.asm -o frame.bin
; cartconv -t normal -i frame.bin -n 'my cart' -o frame.crt
; x64 -cartcrt frame.crt
;----------------------------------------------------------

;no load-adress for bin-file, so no header here

*=$8000
.word launcher ;cold start
.word launcher ;warm start
.byte $c3	;c
.byte $c2	;b
.byte $cd	;m
.byte $38	;8
.byte $30	;0

launcher
  stx $d016
  jsr $fda3	;prepare irq
  jsr $fd50	;init memory
  jsr $fd15	;init i/o
  jsr $ff5b	;init video
                ;make sure this sets up everything you need,
                ;the calls above are probably sufficient
  ldx #$fb
  txs

;set up starting code outside of cartridge-area
move_starter
  ldx #(starter_end-starter_start)
loop1
  lda starter_start,x
  sta $100,x
  dex
  bpl loop1
  jmp $100
;---------------------------------
starter_start	
  ldx #$40 ;64 pages = 256 * 64 = 16384 Bytes
  ldy #0
loop
src
  lda exomized_data,y
dst
  sta $801,y
  iny
  bne loop
  inc src+2-starter_start+$100 
  inc dst+2-starter_start+$100
  dex
  bpl loop

;make sure settings for $01 and IRQ etc are correct for your code
;remember THIS table from AAY64:

;       Bit+-------------+-----------+------------+
;       210| $8000-$BFFF |$D000-$DFFF|$E000-$FFFF |
;  +---+---+-------------+-----------+------------+
;  | 7 |111| Cart.+Basic |    I/O    | Kernal ROM |
;  +---+---+-------------+-----------+------------+
;  | 6 |110|     RAM     |    I/O    | Kernal ROM |
;  +---+---+-------------+-----------+------------+
;  | 5 |101|     RAM     |    I/O    |    RAM     |
;  +---+---+-------------+-----------+------------+
;  | 4 |100|     RAM     |    RAM    |    RAM     |
;  +---+---+-------------+-----------+------------+
;  | 3 |011| Cart.+Basic | Char. ROM | Kernal ROM |
;  +---+---+-------------+-----------+------------+
;  | 2 |010|     RAM     | Char. ROM | Kernal ROM |
;  +---+---+-------------+-----------+------------+
;  | 1 |001|     RAM     | Char. ROM |    RAM     |
;  +---+---+-------------+-----------+------------+
;  | 0 |000|     RAM     |    RAM    |    RAM     |
;  +---+---+-------------+-----------+------------+

  lda #$35 ;cart is always on instead of BASIC unless it can be switched off via software
  sta $01
  jmp $80d ;for exomizer, i.e.

starter_end
;----------------------------------
exomized_data
.bin 2,0,"data.exo"
;syntax for exomizer 2.0.1:
;exomizer sfx sys game.prg -o data.exo
main_file_end
;fill up full $4000 bytes for bin file ($c000-$8000=$4000)
.dsb ($c000-main_file_end),0

Exomiser info

 Reading "8085.prg", loading from $0801 to $4CE9.
 Crunching from $0801 to $4CE9.
Phase 1: Instrumenting file
-----------------------------
 Length of indata: 17640 bytes.
 [building.directed.acyclic.graph.building.directed.acyclic.graph.]
 Instrumenting file, done.

Phase 2: Calculating encoding
-----------------------------
 pass 1: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80273.0 bits ~10035 bytes
 pass 2: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80039.0 bits ~10005 bytes
 pass 3: optimizing ..
 Calculating encoding, done.

Phase 3: Generating output file
------------------------------
 Encoding: 1101112133423160,1122,2010223445667788,032144406789BBCD
 Length of crunched data: 10034 bytes.
 Crunched data reduced 7606 bytes (43.12%)
 Target is self-decrunching C64 executable,
 jmp address $0820.
 Writing "data.exo" as prg, saving from $0801 to $304C.
Memory layout:   |Start |End   |
 Crunched data   | $07E7| $2F18|
 Decrunched data | $0801| $4CE9|
 Decrunch table  | $0334| $03D0|
 Decruncher      | $00FD| $01C0| and $9F,$A7,$AE,$AF
 Decrunch effect writes to $DBE7.
Decruncher:  |Enter |During|Exit  |
 RAM config  |   $37|   $37|   $37|
 IRQ enabled |     1|     1|     1|

UPDATE:20230126

; CODE COPY FROM http://www.lemon64.com/forum/viewtopic.php?t=60786&sid=2559442c8b963d7aac27cb13b493f372
; Thanks for posting: Richard of TND
; this is for a 16KB cart, using ACME!! 

      !to "mycart.crt",cart16crt 

scr = $0400 

DecrunchADDR = 2061 ;SYS 2061   (HEX $080D) 

      *=$8000 
      !word launcher 
      !word launcher 
      !byte $c3,$c2,$cd,$38,$30 ;CBM 80 
      
      

launcher 
   sei 
   stx $d016 
   jsr $fda3 ;prepare irq 
   jsr $fd50 ;input memory 
   jsr $fd15 ;initialise i/o 
   jsr $ff5b ;initialise video memory 
 
;For a more professional boot up. Make 
;the border and screen black. AFTER 
;the video memory, etc has finished. 

   lda #$00 
   sta $d020 
   sta $d021 
   cli 

;Switch off the screen. 

   lda $d011 
   and #%11101111 
   sta $d011 

;Move transfer code over to the screen 
;memory. 

   ldx #$00 
tloop   lda transfer,x 
   sta scr,x 
   inx 
   bne tloop 
   jmp scr 

transfer 
   ldx #$00 
tr1      lda linkedgame,x         ;Move from linked address 
  sta $0801,x                        ;Direct to BASIC start address 
   inx 
   bne tr1 
   inc scr+4 
   inc scr+7 
   lda scr+4 
   bne transfer 
   jsr $e453 ;load basic vectors 
   jsr $e3bf ;init basic ram 

   ldx #$fb 
   txs 

   ;Execute the game, by jumping to the 
   ;de-cruncher's start address. 
   ;jmp $0820 
   jmp DecrunchADDR


;Link crunched game as a PRG file to memory after 
;the cartridge build code. 

linkedgame 
   !bin "8085sys.prg",,2 

FileSize = * 
!if FileSize >$c000 { 
!error "FILE SIZE IS TOO BIG TO FIT 16KB CARTRIDGE" 
} else { 

   *=$c000 
} 

Exomizer:

exomizer sfx sys  8085.prg -o 8085sys.prg
 Reading "8085.prg", loading from $0801 to $4CE9.
 Crunching from $0801 to $4CE9.
Phase 1: Instrumenting file
-----------------------------
 Length of indata: 17640 bytes.
 [building.directed.acyclic.graph.building.directed.acyclic.graph.]
 Instrumenting file, done.

Phase 2: Calculating encoding
-----------------------------
 pass 1: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80273.0 bits ~10035 bytes
 pass 2: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80039.0 bits ~10005 bytes
 pass 3: optimizing ..
 Calculating encoding, done.

Phase 3: Generating output file
------------------------------
 Encoding: 1101112133423160,1122,2010223445667788,032144406789BBCD
 Length of crunched data: 10034 bytes.
 Crunched data reduced 7606 bytes (43.12%)
 Target is self-decrunching C64 executable,
 jmp address $0820.
 Writing "8085sys.prg" as prg, saving from $0801 to $304C.
Memory layout:   |Start |End   |
 Crunched data   | $07E7| $2F18|
 Decrunched data | $0801| $4CE9|
 Decrunch table  | $0334| $03D0|
 Decruncher      | $00FD| $01C0| and $9F,$A7,$AE,$AF
 Decrunch effect writes to $DBE7.
Decruncher:  |Enter |During|Exit  |
 RAM config  |   $37|   $37|   $37|
 IRQ enabled |     1|     1|     1|
exomizer sfx $\0801 8085.prg -o 8085out.prg
 Reading "8085.prg", loading from $0801 to $4CE9.
 Crunching from $0801 to $4CE9.
Phase 1: Instrumenting file
-----------------------------
 Length of indata: 17640 bytes.
 [building.directed.acyclic.graph.building.directed.acyclic.graph.]
 Instrumenting file, done.

Phase 2: Calculating encoding
-----------------------------
 pass 1: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80273.0 bits ~10035 bytes
 pass 2: optimizing ..
 [finding.shortest.path.finding.shortest.path.finding.shortest.pat]
  size 80039.0 bits ~10005 bytes
 pass 3: optimizing ..
 Calculating encoding, done.

Phase 3: Generating output file
------------------------------
 Encoding: 1101112133423160,1122,2010223445667788,032144406789BBCD
 Length of crunched data: 10034 bytes.
 Crunched data reduced 7606 bytes (43.12%)
 Target is self-decrunching C64 executable,
 jmp address $0801.
 Writing "8085out.prg" as prg, saving from $0801 to $304C.
Memory layout:   |Start |End   |
 Crunched data   | $07E7| $2F18|
 Decrunched data | $0801| $4CE9|
 Decrunch table  | $0334| $03D0|
 Decruncher      | $00FD| $01C0| and $9F,$A7,$AE,$AF
 Decrunch effect writes to $DBE7.
Decruncher:  |Enter |During|Exit  |
 RAM config  |   $37|   $37|   $37|
 IRQ enabled |     1|     1|     1|

This looks okay: (monitor in vice)

Attaching crt in vice

Maybe one of these problems:

1) you CAN NOT use BASIC routines when a cart is inserted (without weird tricks, i.e.
storing BASIC routines on cart etc)

2) you need to be careful about $01 as you may map in ROM at $8000 without expecting it.

Please refer to this if in doubt:
http://unusedino.de/ec64/technical/aay/c64/memcfg.html

[3] You should also be careful about the usage of KERNAL routines as some of them
sweep across BASIC-code as well!

Altair 8800

After a whole day soldering yesterday, ending up with a wire mess.
Which didn’t work at the end…

Starting measuring some things, and create some test sketches (led blinky tests)
I found out that the main problem was not having the red switches connected to GND.
Blue switches where upside down, this was a easy fix. Because these are ON-ON switches, and where already connected to a common line.
Then a mixup between D0 and D6 (wires crossed)
And it is working! Made some lines and lettering on the frontplate after some playing around.

Weird to input stuff in octal (group of 3 bits)

DIY 6502 – VIC (Versatile Interface Adapter)

65c22 connected, new data, and address-bus ribboncables!

First led on Register B blinking!

Notes:
Temporary display wil be 2×16 Chars.
Ram in place, but not connected (is emulated by the Arduino Mega at the moment)
Rom is somewhere halfway the atlantic ocean .. still waiting on that one.
Ben Eatons clock module is disconnected, i’m using the Arduino as programmable clock right now.
(There wil be a little display and a rotary encoder to set clock speed.)

lda #$ff ; all bits
sta $6002 ; set direction (out) for B register
lda #$80 ; set 1 bit
sta $6000 ; set register B
lda #$00 ; reset bit
sta $6000 ; set register B
jmp $8005 ; jmp to bit set part

6502 and Arduino (due to missing components)

(Work in process, will certainly change)

Due to eeproms being scarce, i’m going to use a arduino as Rom emulator.
Below is a test setup i’m going to build.

Made the drawing in Kicad.

KiCad is a free software suite for electronic design automation. It facilitates the design and simulation of electronic hardware. It features an integrated environment for schematic capture, PCB layout, manufacturing file viewing, SPICE simulation, and engineering calculation.

Memory assignment:

$8000-FFFF - Rom
$4000-7FFF - Ram ?
$2000-3FFF - Multiple times the 6522 *
$0000-???? - Ram probably

* This is due to the fact i am only using Address lines: 0,1,2,3,13,14,15

Mystery Cartridge C64

As posted before, i found a cartridge in my collection a while ago. I don’t know where i’ve got this one from.

While searching on the internet for more information, i really couldn’t find anything about it.
Not even on collectors sites.

Where did it come from, what does it do?

It says: Data Manager 64 (1984) , Softworld and Handic.

I can find many cartridges by Handic. I’ve checked all database/filers i could find. Non looked like this.

Next to try ..
Dumping the cartridge to file. Probably by changing the way the C64 starts a autostart cartridge.
So i have to modify the cartridge port, and put a cross connection on the lines EXROM and GAME, this will change the memory addresses ($8000/$A000) where a cartridge is placed. Then the commodore will start normally, and i can dump the cartrige memory locations to a file.

So pins 8 and 9 have to be switched around.
The C64 autostarts a cartridge when it find certain data on $8000

Meanwhile i try to contact some collectors of cartridges.

I don’t think its a homebrew, but we will see.

Mini C64 for MCH2022

Cool little C64 gadget for MCH2022

Next week i’ll be going to May Contain Hackers 2022, what to bring?
My old friend Bigred will be there, many others couldn’t make it …

What to bring and do:

  • Laptops
  • Arduino touch project?
  • My new 6502 breadboard computer?
  • The DVB-T / DAB / FM stick
  • Besides the emulators on my laptop, maybe i’ll bring this little thing
    (Or a real C64?)

Booting in 4 seconds!
Running Vice in 50 or 60 Hz
Low latency video!
Can emulate cartrides, floppy’s
When you connect joysticks or a real C64 keyboard to the GPIO pins it will use that.
SID sound using ReSid
CRT emulation
(look for BMC64 or combian)

C64/6502 and Assembly

I’ve used a basic program on C64 in the past and a Cartridge machinecode monitor in the past.
I’ve really forgotten how, what i’ve used and what i’ve done with it.
Not nearly as much as my friends at that time.
I started with a Vic-20 and played around with machinecode on a 6502.
I didn’t have a C64 for many years.

I’ve recently started to build a 6502 computer again, and programming on 65xx again (Generic 6502 and C64). (2022)

Below is my setup on linux, to write assembly code, compiling and running the code in a emulator.

I have installed the Acme compiler and Vice as a emulator.
Both can compile/run machinecode for multiple computer emulations. So maybe i can run my old Vic-20 machine code or the few C64 programs i’ve written.

I’ve only made the bash script, the included asm files i copied from someone on the internet. ( Credit lookup )

makeprg bash file:

#!/bin/bash
set -x
f=""
if [ "$2" == "f" ] ; then f="-fullscreen" ; fi 
if [ ! -f $1.asm ] ; then
cp template.asm $1.asm
fi
vi $1.asm
acme --cpu 6510 --format cbm --outfile $1.prg $1.asm
if [ ! $? -eq 0 ] ; then exit 1 ; fi 
c1541 -format foo,id d64 $1.d64 -write $1.prg
if [ ! $? -eq 0 ] ; then exit 1 ; fi 
x64 $f $1.prg 

template.asm

!source "basic-boot.asm"

+start_at $0900

; Set background and border to black
ldx #$00
stx bgcol
stx bocol

; Flicker border and background
.loop
  inc bgcol
  inc bocol
  jmp .loop

basic-boot.asm

; A BASIC booter, encodes `10 SYS <address>`.
; Macroified from http://www.pouet.net/topic.php?which=6541

!source "constants.asm"

!macro start_at .address {
  * = basic
  !byte $0c,$08,$00,$00,$9e
  !if .address >= 10000 { !byte 48 + ((.address / 10000) % 10) }
  !if .address >=  1000 { !byte 48 + ((.address /  1000) % 10) }
  !if .address >=   100 { !byte 48 + ((.address /   100) % 10) }
  !if .address >=    10 { !byte 48 + ((.address /    10) % 10) }
  !byte $30 + (.address % 10), $00, $00, $00
  * = .address
}

; A cooler example is to write
;
;   10 SYS <address>: REM <backspaces>Your comment
;
; When the user types LIST, he will just see
;
;   10 Your comment
;
; but still be able to run it.
; For this, see http://codebase64.org/doku.php?id=base:acme-macro-tu

When running above bash script. it will open the file if it exists, else it will take a template file.
After opening it with vi, and editing it, it starts a the compiler and creates a C64 d64 disk.
This is going to be autorun/started with the VIce emulator.
Appending -f to the bash script will start it in fullscreen mode.
./makeprg myawesomedemo.asm -f

Below it is running without the fullscreen option. but is shows how to start the interactive monitor in vice.

  • n – step x instructions
    n 100
  • m monitor
(C:$103e) m
>C:103e  cd 12 d0 d0  fb a2 00 bd  5c 10 bc 79  10 88 d0 fd   ........\..y....
>C:104e  8d 20 d0 8d  21 d0 e8 e0  1d d0 ec 4c  81 ea 06 00   . ..!......L....
>C:105e  0e 06 0e 0e  03 0e 03 03  01 03 01 01  01 01 03 01   ................
>C:106e  03 03 0e 03  0e 0e 06 0e  00 06 00 07  09 09 09 09   ................
>C:107e  09 09 09 07  09 09 09 09  09 09 09 07  09 09 09 09   ................
>C:108e  09 09 09 07  09 09 09 09  00 00 00 00  ff ff ff ff   ................
>C:109e  00 00 00 00  ff ff ff ff  00 00 00 00  ff ff ff ff   ................
>C:10ae  00 00 00 00  ff ff ff ff  00 00 00 00  ff ff ff ff   ................
>C:10be  00 00 00 00  ff ff ff ff  00 00 00 00  ff ff ff ff   ................
(C:$10ce) 
  • d -assemble
(C:$1041) d 1000
.C:1000  78          SEI
.C:1001  A5 00       LDA $00
.C:1003  8A          TXA
.C:1004  98          TYA
.C:1005  8D 20 D0    STA $D020
.C:1008  8D 21 D0    STA $D021
.C:100b  A0 7F       LDY #$7F
.C:100d  8C 0D DC    STY $DC0D
.C:1010  8C 0D DD    STY $DD0D
.C:1013  AD 0D DC    LDA $DC0D
.C:1016  AD 0D DD    LDA $DD0D
.C:1019  A9 01       LDA #$01
.C:101b  8D 1A D0    STA $D01A
.C:101e  A9 39       LDA #$39
.C:1020  A2 10       LDX #$10
.C:1022  8D 14 03    STA $0314
.C:1025  8E 15 03    STX $0315

Etc .. see https://vice-emu.sourceforge.io/vice_12.html

New 6502 DIY computer

Started with a new Micro computer project.

Update: 20220721 .. VIA chip installed
Update: 20220801 .. changed layout, addressing and added rom, see below post.

Such a influencial little processor … Apple, Vic-20, C64 (with modifications), PET, BBC Micro, Oric, Atari and Nintendo.

Another (big brother) influencial CPU is the 68000.
(Amiga/Atari ST/ Macintosh/Sinclair)

I’ve made a 680x computer in the past, and i want to make another one.

This one will be based on a 6502, because i used to program on this cpu when i got my Vic-20.

Goals of this project:

  • 6502 Cpu
  • Memory and Rom
    • Rom must contain a good machinecode monitor
  • Adjustable clock
    • Now using Ben Eatons clock diagram, but i will move this to a programmable arduino, with a display which shows the clock rate
  • Hex keyboard ro program the machine, just like picture below
  • Display which was a resolution of at least 640×480
    • It will be a slow screen, character printing and a gfx mode?
    • First probably a SPLC780 HD44780, so i can enter/edit machine code.
  • Hopefully using a SID chip
  • Hardware monitoring of the address and data lines like movie below
  • Programming via serial/usb, by halting the 6502 cpu and pushing data into memory or fake-eprom with a arduino
    • Save/restore by modifying memory
  • Small
Example of Hex keyboard

Update 20220721

Via chip is on the board.
For now i’m using a old display, like this one

I was planning to use this one

The graphic capable 12864 display (128 x 64 pixels)

I will reuse the schematics i’ve used for the 680x computer. (Posted above)

Update: 20220801

Added ROM, and changed layout.
Every breadboard has a function now.

Upper left, Ben’s Clock module (this is going to be changed to a arduino with display which shows frequency)
Upper right, power-on reset (Reused part of C64 schematic)
Second row left, the 6502
On the right the ROM, RAM i also going to install here.
3rd Row, Address decoding, this is going to be a dynamic setup using dip switches and a eeprom for decoding (i know, this kind of decoding is slow, but i don’t need speed), on the right probably the hex keyboard with its own 6522.
4rd row left, a temporary display 16X2 connected via a 6522. Here i want to have a graphical display.
4rd row right (not started this part yet) a sound device. SID or a Yamaha sound chip i still have.

Screens and DIY projects

Below some examples and connection diagrams to control displays.
More code and complete schematics will be added on this page or on a separate projects page.

UPDATE 20230119 Cost of 20×4 display in 1998

LCD

I’ve used a LCD display like this (HITACHI HD44780) on my PC in the 90s, and also written code to use this as a monitoring device on my amiga.

On Linux i used LcdProc – This module also was equiped with a serial connector
Now (2023) it is 8 euros!
When bought now fl to euro 98 Euro or 107 $
;LCD Display Module             Parallel port
;        1 Vss                  20 GND
;        2 Vdd                  14 +5V
;        3 Vlc                  20 GND (contrast LCD display)
;        4 RS (register select) 11 BUSY
;        5 R/W                  12 POUT
;        6 E (enable)           13 SEL
;        7 DB0                   2 D0
;        8 DB1                   3 D1
;        9 DB2                   4 D2
;       10 DB3                   5 D3
;       11 DB4                   6 D4
;       12 DB5                   7 D5
;       13 DB6                   8 D6
;       14 DB7                   9 D7
Amiga code part
        bsr     initprt         ; CIA 8520 init
        bsr     initlcd         ; init lcd display module
        move.l  #0,d0
        rts

initprt:move.b  #$ff,$bfe301    ; parallel port is output
        move.b  $bfd200,d0
        ori.b   #$07,d0         ; select, p-out and busy
        move.b  d0,$bfd200      
        rts

initlcd:move.w  #$38,d0         ; multiple reset
        bsr     send
        bsr     delay2
        move.w  #$38,d0
        bsr     send
        bsr     delay2
        move.w  #$38,d0         ; 2*8 lines
        bsr     send
        bsr     delay2
        move.w  #$01,d0         ; clear display
        bsr     send
        bsr     delay2          ; wait
        move.w  #$0c,d0         ; display on
        bsr     send
        move.w  #$06,d0         ; Entry Mode Set
        bsr     send
        rts

send:   bsr     delay
        btst    #8,d0           ; test rs bit
        beq     reg0
        bsr     rs1             ; select register 1
        bra     skip
reg0:   bsr     rs0             ; select register 0
skip:
        bsr     delay
        bsr     rw0             ; read/write=0 
        bsr     delay
        bsr     e1              ; enable = 1
        bsr     delay
        move.b  d0,$bfe101      ; push data
        bsr     delay
        bsr     e0              
        bsr     delay
        rts

delay:  move.w  #$20,d1
dloop:  subi    #1,d1
        bne     dloop
        rts

delay2: move.w  #$800,d1
dloop2: subi    #1,d1
        bne     dloop2
        rts
Part of my MQTT display alarm thingy
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Wire.h> 
#include <LiquidCrystal_I2C.h>  
LiquidCrystal_I2C lcd(0x27, 20, 4);
const char* ssid = "MYACCESSPOINT";
const char* password = "MYPASSWORD";
const char* mqtt_server = "mymqttserver";
const byte ledRed = 12;
const byte horn = 13;
int button = 2;
int press = 0;
boolean buttonToggle = true;


// Todo : DISPLAY 2ND LINE, DISPLAY SILENT, ...

WiFiClient espClient;
PubSubClient client(espClient);
bool toggle = false;
void setup_wifi() {
  delay(100);

  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  randomSeed(micros());
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

void callback(char* topic, byte* payload, unsigned int length)
{
  if (length > 0) {
    toggle = true;
  }

  if (length == 0) {
    toggle = false;
  }

  Serial.print("Command from MQTT broker is : [");
  Serial.print(topic);

  Serial.println();
  Serial.print(" publish data is:");
  lcd.clear();
  lcd.backlight(); // turn off backlight

  {
  
    for (int i = 0; i < length; i++)
    {
      Serial.print((char)payload[i]);
      if (i < 16){
      lcd.setCursor(0, 0);
      lcd.setCursor(i, 0);
      } else {
      lcd.setCursor(0, 1);
      lcd.setCursor(i-16, 1);
      }
      lcd.write((char)payload[i]);
    }
  }


  Serial.println();
} 

void reconnect() {
  
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    
    String clientId = "mqttlcd";
    clientId += String(random(0xffff), HEX);

    if (client.connect(clientId.c_str()))
    {
      Serial.println("connected");

      client.subscribe("mqttlcd/message");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(6000);
    }
  }
} 

void setup() {
  Serial.begin(115200);
  pinMode(button, INPUT);
  digitalWrite(2, HIGH);
  pinMode(ledRed, OUTPUT);
  digitalWrite(ledRed, LOW);
  pinMode(horn, OUTPUT);
  digitalWrite(horn, LOW);
  setup_wifi();
  client.setServer(mqtt_server, 1883);
  lcd.init(); 
  lcd.backlight();
}

void loop() {

  
  if (!client.connected()) {
    reconnect();
  }
  if (toggle == true) {
    digitalWrite(ledRed, HIGH);
    digitalWrite(horn, HIGH);
    delay(200);
    digitalWrite(ledRed, LOW);
    digitalWrite(horn, LOW);
    delay(200);
  }
  if (toggle == false) {
    digitalWrite(ledRed, LOW);
    digitalWrite(horn, LOW);

  }

  client.setCallback(callback);
  client.loop();

  press = digitalRead(button);
  if (press == LOW)
  {
    if (buttonToggle)
    {
      digitalWrite(ledRed, HIGH);
      digitalWrite(horn, HIGH);
      buttonToggle = !buttonToggle;
    }
    else
    {
      digitalWrite(ledRed, LOW); 
      digitalWrite(horn, LOW);
      buttonToggle = !buttonToggle;
      toggle = false;
      client.publish("mqttlcd/button","pressed");
      lcd.clear();
      lcd.noBacklight(); // turn off backlight
    }
  }
  delay(500);  //delay for debounce
}

Oled

There are several oled displays, mostly controllable with i2c but some of them are SPI

SSD1306 – I2c connected

Using a wemos – Octoprint project for example
Octoprint (Note: this is NOT a multicolor display 1/4 of the display is yellow. )
My notification watch. Runs on a ESP12F connects to Wifi, has a piezo sound element
Using a raspberry (Part of my Lab Sensors Project)
pip3 install adafruit-circuitpython-ssd1306
git clone https://github.com/adafruit/Adafruit_Python_SSD1306 (old)
Edit file - comment SPI section

Some arduino’s have embedded displays like those i’ve used for a Lora project.

Other means of connecting : SPI

SPI connected display

Nextion

Nextion is a Human Machine Interface (HMI) solution combining an onboard processor and memory touch display with Nextion Editor software for HMI GUI project development.

Using the Nextion Editor software, you can quickly develop the HMI GUI by drag-and-drop components (graphics, text, button, slider, etc.) and ASCII text-based instructions for coding how components interact on the display side.

Nextion HMI display connects to peripheral MCU via TTL Serial (5V, TX, RX, GND) to provide event notifications that peripheral MCU can act on, the peripheral MCU can easily update progress, and status back to Nextion display utilizing simple ASCII text-based instructions.

My nextion domoticz box, tilt to wakeup
Domoticz controller

My biltong box using a Nextion

Raspberry displays

 3.5inch RPi Display – 480×320 Pixel – XPT2046 Touch Controller
edit cmdline.txt
add "fbcon=map:10 fbcon=font:ProFont6x11 logo.nologo"
at the end
edit config.txt
add between custom comments at the bottom
dtoverlay=piscreen,speed=24000000,rotate=90
# Or check http://www.lcdwiki.com/3.5inch_RPi_Display

Above display’s i’ve used for Picore Players and the Lidar POC

To try: Getting above display running with a arduino
https://github.com/PaulStoffregen/XPT2046_Touchscreen

Raspberry HDMI display

Easiest of them all, just connect with HDMI, there is a adaptor for hdmi-hdmi (versions 1,2,3) and hdmi-mini-hdmi for RPi4 variants.

Epaper and 7-Segment displays

Other means of displaying information are for example

Epaper

ESP with epaper module, disconnected power for a while, artifacts appear.

7 Segment displays

I used a lot of 7-Segment display’s in the past. They look cool and are hardcore.

My homebrew computer uses this

Nixie tubes!

And there are https://en.wikipedia.org/wiki/Nixie_tube .. I’ve never had those

Above bigger 2D display i used with Wled and a digital microphone, so its sound reactive. The lower part i got in recently .

inmp441 digital microphone