commit 73f22f5fce957b040cfa64cc447d90f13129e678 Author: Hexa Dust Date: Mon Oct 20 20:08:01 2025 +0100 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..5bfc2d4 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# RC2014 - Quazar OLED board + +This project provides MACRO-80 assembler code for `oled` program to drive OLED display for RC2014 by Quazar. It provides a Rust "script" to convert 128x32 black and white image to hex output suitable for the `oled` program. + +## Compiling + +``` +m80 =oled.mac/z +l80 oled,oled/n/e +``` + +## Running + +``` +oled +``` + +Now paste your image hex data as generated by `oled-to-hex`. diff --git a/oled-to-hex b/oled-to-hex new file mode 100755 index 0000000..ba7cc68 --- /dev/null +++ b/oled-to-hex @@ -0,0 +1,58 @@ +#!/usr/bin/env -S denim +/* Cargo.toml +[package] +name = "oled-to-hex" +version = "0.1.0" +authors = ["Anonymous"] +edition = "2021" + +[dependencies] +image = { version = "0.25.8", default-features = false, features = ["png"] } +*/ + +use image::GenericImageView; +use image::ImageReader; +use image::Pixel; +use std::env; +use std::error::Error; +use std::io::{Error as IoError, ErrorKind}; + +/// Example script description +fn main() -> Result<(), Box> { + let file = env::args().skip(1).next().ok_or(IoError::new( + ErrorKind::NotFound, + "expected PNG 128x32 file path", + ))?; + println!("Loading {}", file); + + let img = ImageReader::open(file)?.decode()?; + let (w, h) = img.dimensions(); + println!("Loaded file {}x{}", w, h); + if w != 128 && h != 32 { + return Err(Into::into(IoError::new( + ErrorKind::InvalidData, + "bad immage dimensions", + ))); + } + + for row in 0..4 { + for column in 0..128 { + let mut byte = 0u8; + for bit in 0..8 { + byte = byte >> 1; + let x = column; + let y = row * 8 + bit; + let pixel = img.get_pixel(x, y); + let luma = pixel.to_luma()[0]; + if luma > 128 { + byte = byte | 128; + } + } + print!("{:02X}", byte); + } + } + + Ok(()) +} + +// vim: ft=rust diff --git a/oled.mac b/oled.mac new file mode 100644 index 0000000..6554eb5 --- /dev/null +++ b/oled.mac @@ -0,0 +1,198 @@ +.Z80 +ASEG ; absolute addressing +ORG 100h ; skip CP/M + +CR EQU 0Dh ; carriage return +LF EQU 0Ah ; new line +BDOS EQU 0005h ; BDOS entry point +PSTR EQU 09h ; print string +CRED EQU 01h ; read console + +OLED EQU 50h ; base addr of OLED card +ORST EQU 04h ; reset command (H) +OBUF EQU 02h ; chip select (L), buffer (H) +ODB EQU 01h ; display byte (H) +OCMD EQU 00h ; command (L) + +; 4 rows of display +OROWA EQU 0B0h ; select top row +OROWB EQU 0B1h ; select second row +OROWC EQU 0B2h ; select third row +OROWD EQU 0B3h ; select bottom row + +OCOL0 EQU 04h ; left most column +OSELC EQU 10h ; select column command +OCOLL EQU 80h ; column count: 128 columns + +; 128 columns from 4 to 131 +; 1Xh, 0Yh - XY = 04h to 83h + +; MAIN +CALL OINIT ; init OLED +CALL OCLS ; clear screen +CALL PROMPT ; print prompt + +CALL OCSTART ; go to start +CALL READLINE ; read row A +CALL READLINE ; read row B +CALL READLINE ; read row C +CALL READLINE ; read row D + +RET ; exit to CP/M + + +READLINE: + LD D,OCOLL ; number of columns +READLINE1: + PUSH DE + CALL READHEX ; read hex input from console + CALL ODATA ; write to OLED + POP DE + DEC D ; count donw columns + JP NZ,READLINE1 ; next column + RET + +OINIT: + CALL ORESET ; reset OLED + LD HL,OINITSEQ ; commands to send +OINITLOOP: + LD A,(HL) ; load command byte to A + CP 0FFh ; check if we are done + RET Z ; marker reached + CALL OCOMMAND ; send command from A + INC HL ; next command + JR OINITLOOP ; send next command + +ORESET: + LD C,OLED ; base I/O address + LD B,ORST OR OBUF ; RST H, /CS H, DB L + OUT (C),A ; write any A, to (C) + B + REPT 4 + NOP ; delay + ENDM + LD B,OBUF ; clear RST + OUT (C),A ; send + REPT 20 + NOP ; long delay + ENDM + RET + +OCLS: + CALL OCSTART ; set start + CALL OCLSROW ; clear row A + CALL OCLSROW ; clear row B + CALL OCLSROW ; clear row C + CALL OCLSROW ; clear row D + RET + +; set cursor to start +OCSTART: + LD A,OSELC ; select column + CALL OCOMMAND + LD A,OCOL0 ; column 0 + CALL OCOMMAND + LD A,OROWA ; select first row + CALL OCOMMAND + RET + +OCLSROW: + ;XOR A ; display byte to write (0) + LD A,0FFh ; turn on all pixels + LD D,OCOLL ; count columns in D +OCLSROW1: + CALL ODATA ; write A to displey + DEC D ; next column + JR NZ,OCLSROW1 + RET + +; A = command to send to OLED +OCOMMAND: + LD C,OLED ; base I/O + LD B,OCMD ; send command + OUT (C),A ; send + REPT 4 + NOP + ENDM + LD B,OCMD OR OBUF + OUT (C),A ; send + REPT 10 + NOP + ENDM + RET + +; Write to OLED memory +; A = graphic byte to dispaly memory +ODATA: + LD C,OLED ; base I/O + LD B,ODB ; select Data Byte mode + OUT (C),A ; output byte + REPT 4 + NOP + ENDM + LD B,OBUF OR ODB + OUT (C),A ; output byte + NOP + NOP + RET + +; Read byte from console +READ: + LD C,CRED ; read from console + CALL BDOS ; call CP/M + RET ; A=CHAR + +; Reads 4 bit value (0-F) +READNIB: + CALL READ + LD B,41h ; compare with 'A' (10) + CP B + JP NC,RNB1 ; A>=B + SUB 30h ; sub '0' + RET +RNB1: + SUB 41h-10 ; sub 'A'-10 + AND 0Fh ; clear to 4 bits so 'a-f' also work + RET + +; Reads two bytes (0-F) from console +; A = byte read +READHEX: + CALL READNIB ; read MSD nibble + SLA A ; shift 4 bits + SLA A + SLA A + SLA A + LD B,A ; B=A + PUSH BC ; save B + CALL READNIB ; read LSD nibble + POP BC ; restore B + OR B ; OR B to A + RET + +PROMPT: + LD C,PSTR ; load function number + LD DE,PROMPTMSG ; load string addr + CALL BDOS ; call CP/M + RET + +PROMPTMSG: + DB 'OLED:>', CR, LF,'$' ; string terminated with $ + +; Initialization sequence +; 081h,080h - set contrast (080h full) +; 020h,000h - set horizontal window wrapping +; 021h,004h,083h - window full width +; 022h,000h,003h - window full height +OINITSEQ: + DB 0AEh,0D5h,0A0h,0A8h + DB 01Fh,0D3h,000h,0ADh + DB 08Eh,0D8h,005h,0A1h + DB 0C8h,0DAh,012h,091h + DB 03Fh,03Fh,03Fh,03Fh + DB 081h,070h,0D9h,0D2h + DB 0DBh,034h,0A6h,0A4h + DB 0AFh,020h,000h,021h + DB 004h,083h,000h,003h + DB 0FFh ; end marker + +END diff --git a/oled_compute.png b/oled_compute.png new file mode 100644 index 0000000..9baf787 Binary files /dev/null and b/oled_compute.png differ diff --git a/oled_cpm.png b/oled_cpm.png new file mode 100644 index 0000000..b8df034 Binary files /dev/null and b/oled_cpm.png differ diff --git a/oled_tog_hxd.png b/oled_tog_hxd.png new file mode 100644 index 0000000..afa404b Binary files /dev/null and b/oled_tog_hxd.png differ diff --git a/oled_tog_hxd_grad.png b/oled_tog_hxd_grad.png new file mode 100644 index 0000000..3b55143 Binary files /dev/null and b/oled_tog_hxd_grad.png differ diff --git a/oled_zilog.png b/oled_zilog.png new file mode 100644 index 0000000..99ca6dd Binary files /dev/null and b/oled_zilog.png differ