Tag Archives: programming

Boot loader with image 320×200 256 colors.

This is a work in progress, below are my Lab notes.

I want to rewrite pieces we made for a demo, loading images and effects from a floppydisk bootloader.

Without looking at old code (which was written using Masm), I wanted to learn the steps using Fasm.

I started with a boot sector program, It should do the following.

  • Set graphic mode, and start a trackloader
  • Load sector 2 and 3 which contains the color palette for the image.
  • Next sectors, cylinders and heads contain the raw image

I got it working, half that is.
In the past I used real disks, and now a virtual disk, maybe thats the difference?

First Code

use16
org 0x7c00

mov ah,0x0
mov al,0x13
int 10h

mov ax, 0a000h
mov es, ax
xor di, di
mov ax, 50
mov cx, 64000
rep stosb

loophere:
	jmp loophere


times 510 - ($-$$) db 0

dw 0xaa55

Compiling and starting:

fasm mybootblock.asm
qemu-system-x86_64 --drive format=raw,file=mybootblock.bin

This works, it sets the graphical mode and clears the screen.

Second Code
Skipping the int 25h version

## Track read part
    xor ax, ax    ; DS = 0
    mov ds, ax
    cld
    mov ah, 2h    ; int13h function 2 track read
    mov al, 2     ; number of tracks ( should be 2 for reading only palette)
    mov ch, 0     ; from cylinder number 0
    mov cl, 2     ; the sector number 2 - second sector (starts from 1, not 0)
    mov dh, 0     ; head number 0
    xor bx, bx    ; BX = 0
    mov es, bx    ; ES = 0
    mov bx, 7e00h ; Offset from above
    int 13h

    call setpal

## End part with setpalette routine
## appending palette.colors
## and a raw image

setpal:	
    mov dx,3c8h
    xor al,al
    mov di, ax
    out dx,al
    inc dx
    mov cx,256*3
    mov si,07e00h
    rep outsb
    ret
times 510 - ($-$$) db 0

dw 0xaa55
include 'palette.colors'

times 2048 - ($-$$) db 0
file 'image.raw'

Seems there is still a header on the RAW file, lets look at how I made this.

NOTE! .. Below converts an image with a STANDARD VGA palette, not a custom one as used above

Looking with ghex at the file I saw that there was a header 0x415 bytes large.
(Probably still palette colors in there)

dd if=shoes.bmp of=cutshoe.bmp bs=1 skip=1078 (0x415h + 3?)
worked for me

Loading the extra tracks didn’t work for me?!?!
But how could I define tracks/sectors and heads on a virtual floppy?

I tried to write sectors using debug.com

start dosbox
imgmount a: /tmp/floppy.img -t floppy
debug.com bootsector.bin
-r bx 01 

-r cx 512
; set bx:cx for size
-w 100 0 0 1
; write from address 100, drive 0 (a), sector 0, number of sectors

; testing
-l 100 0 0 1
;load sector to addr 100 drive 0 sector 0 number of sectors 

This used to work with real disks on a real machine, not in dosbox ?!?!

my way to create a disk in linux

dd if=bootblock.bin of=disk1.img bs=512 count=1 seek=0
dd if=palette.col of=disk1.img bs=512 count=1 seek=1 # or 2?
dd if=shoes.raw of=disk1.img bs=512 count=10000 seek=17

It looks like I can’t read futher than 18 sectors on a virtual floppy.
What next? Head=1? Cylinder=1?
Below the info from a floppy image before altering.

DOS/MBR boot sector, code offset 0x3c+2, OEM-ID “MSDOS5.0”, root entries 224, sectors 2880 (volumes <=32 MB), sectors/FAT 9, sectors/track 18, serial number 0x1c2a0d0f, unlabeled, FAT (12 bit), followed by FAT

Appending the RAW to a executable gave me problems to.
(Without making a bootdisk)

Above and below weird data. Appending the data to the executable needs some work also.

At least for today let me display this image correctly 🙂

use16
org 0x100

; set mode 320x200 256 colors palette
	mov ah,0x0
	mov al,13h
	int 10h

; clear screen routine, not really needed
clearscreen:
	push ax
	mov ax, 0a000h
	mov es, ax
	pop ax
	xor di, di
	inc ax
	mov cx, 64000 ; 320x200
	rep stosb

; call file loader 
	call Loadfile

; after displaying the image or displaying an error, wait for keypress to exit
waitforkeyloop:
	MOV AH,1
	INT 16h
	JZ waitforkeyloop
	XOR AH,AH
	INT 16h
Exit:
	MOV AX,3	; default text mode 3
	INT 10h
	MOV AX,4C00h	; exit to dos (terminate process)
	INT 21h

Loadfile:
	MOV DX,filename
	MOV AX,3D00h	; open filehandle
	INT 21h
	JC Err1
	MOV BX,AX   	; filehandle
	MOV CX,0FFFFh 	; size
	mov dx,0a000h 	; destination 0000:a000h - Screen memory
	mov ds,dx

	MOV DX,0
	MOV AH,3Fh	; read from file
	INT 21h
	JC  Err1
	MOV AH,3Eh	; close filehandle
	INT 21h

	RET
; print error
Err1:
	push cs		; make ds same as cs
	pop ds
	MOV DX,TxtErr1	; error
	MOV AH,09h
	INT 21h
	RET

filename DB "shoes.bmp",0
TxtErr1 DB "shoes.bmp not found!",7,10,13,"$"

Tomorrow .. back to the track loader

Mikrotik Wifi, 80386 and Lilygo streaming

Quiet days, I working on some art.

But here are the last ‘prutsen’

My current Wifi setup

I’ve got a Wifi outside of my network for guest and emergency. ( 2 SSIDs)

Then a main Wifi router in my livingroom, one in my workshop/studio and one in the Attic (Electronics Lab)

So three main Wifi AccessPoints. These all have the same SSID’s but on different frequencies. That way i’ve got roaming in and outside my house.
Also some virtual accesspoints are configured.
I’ve got a main, folkband, IOT, guest-inside all on 2.4Ghz and 5Ghz.

I watched a lot of YT presentations about Mikrotik Wifi.

So I ended up with DFS safe channels 20Mhz for 2.4 and 20/40Mhz Ce for 5Ghz. (subchannels for each after some frequency scanning)
(2.4 does a failback to 20Mhz whenever there is even one client detected which connects only on this band. Such as some old IOT stuff)
2.4 in only 1,6 and 11 no overlap, each on another device.
300Mbps is sufficient for my wifi 🙂

I’ve got accesslists in place and i’m going to read into kicking a client when the signal strenght is below -50dB

80386 (DX) Computer

Besides my 8088 and 8086 machines I needed a machine which could run our old demo’s. So I bought a new toy.

It has 8Mb Ram and runs at 40Mhz.

I’ve noticed that many of my VGA register manipulation code, can’t be run on a modern VGA monitor, I need to use a CRT for that .. Another thing to buy

Lilygo T-Display S3 Streaming

Not my code: https://github.com/Steve5451/esp32-stream-desktop
A very cool project!

Needed to fix arduino code, due to the TFT_eSPI library issues.
And I’ve got a S3 with another resolution, but that was an easy fix.
Then needed to reinstall nodejs with another version.
Had to modify the code because the tcp server would not start.
Weird errors logging, but in the end fixed … very cool

I probably end up designing a 3D printed case that looks like a monitor or tv.

Triple screen panorama viewer

Got a question, could I make the video viewer also for images.

Well, that is a great idea, i’ve got some panoramic photos myself.

A little modification, some added code, but here is a working example.

Some vacation pictures widescreen …

CODE
imageview.py filename
Use esc to stop, and enter for next image.
(Has better full screen experience than my movie player. (no padding) have to revisit that one )

Nice to have?

  • Back button?
  • Comments, renaming thumb
from pathlib import Path
from sys import platform as PLATFORM
import os
import re
import PySimpleGUI as sg
from PIL import Image, ImageEnhance, ImageTk, ImageOps, ImageFilter
from xml.etree import ElementTree as ET
import sys
from sys import platform as PLATFORM

abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)

try:
    image=sys.argv[1]
except:
    print(sys.argv[0] +  " filename")
    exit()


def nextFile(currentfile,dir):
        newfile=""
        dirpath = os.path.dirname(dir)
        fileList = []
        for f in os.listdir(dirpath):
            #fpath = os.path.join(dirpath, f)
            fpath = f
            if os.path.isfile(fpath) and f.endswith(('.jpg','.JPG')):
                fileList.append(fpath)
        fileList.sort()
        for i in range(len(fileList)):
            try:
                if (fileList[i]) == currentfile:
                    newfile=fileList[i+1]
                    break
            except:
                newfile=fileList[0]
        return newfile
# yeah i know .. no thumb but full image, change it yourself!
def loadthumb(thumbfile):
    # IF exists
    path_to_file = thumbfile
    path = Path(path_to_file)

    if path.is_file():
        im = Image.open(thumbfile)
        im=ImageOps.contain(im, (5760,5760)) 
        thumbimage = ImageTk.PhotoImage(image=im)

        window['image'].update(data=thumbimage)
    else:
        window['image'].update("")

sg.theme('SystemDefaultForReal')
#------- Layout image only --------#
layout = [
        [[sg.Image('', size=(5760, 1080), key='image',background_color='black',pad=(0, 0))],
          ]]

#------- Set window --------#
window = sg.Window('Triple image player', layout, no_titlebar=True, margins=(0,0),location=(0,0), size=(5760,1080), keep_on_top=True, finalize=True,resizable=False)

window.bring_to_front()
window.Maximize()
window.bind("<Escape>", "-ESCAPE-")
window.bind("<Return>", "-ENTER-")

window['image'].expand(True, True)               

loadthumb(image)
nextfile = image
#------------ The Event Loop ------------#
while True:
    event, values = window.read(timeout=1000)       # run with a timeout so that current location can be updated
    if event == sg.WIN_CLOSED:
        break

    if event == '-ENTER-':
        nextfile = nextFile(nextfile,'./')
        loadthumb(nextfile)

    if event == '-ESCAPE-':
        window.close()
window.close()

Converting images for right resolution from a temp directory filled with large panorama photos

ls temp  | while read; do
	convert -resize 5760x -gravity center  -crop 5760x1080 -auto-orient  "temp/$REPLY" "$REPLY" 
done

Triple screen movie player in python

I didn’t find an easy and working movie player for wide screen setups.
Like double/triple monitor setups.

I’ve got 3x 1920×1080 monitors connected to my battlestation.
With a resolution of 5760×1080

Simple Python code to play a movie fullscreen

# importing vlc module
import vlc
 
# creating vlc media player object
media_player = vlc.MediaPlayer()
 
# media object
media = vlc.Media("movie.mp4")
 
# setting media to the media player
media_player.set_media(media)
media_player.toggle_fullscreen()
 
# start playing video
media_player.play()
 

But trying to get this stable working, I resorted to pysimplegui

#!/usr/bin/env python3

import PySimpleGUI as sg
import vlc
import sys
from sys import platform as PLATFORM

try:
    movie=sys.argv[1] 
except:
    print(sys.argv[0] +  " filename")
    exit()

sg.theme('DarkBlue')

layout = [[sg.Image('', size=(5760, 1080), key='-VID_OUT-')]]
window = sg.Window('Triple movie player', layout, no_titlebar=True, margins=(0,0),location=(0,0), size=(5760,1080), keep_on_top=True, finalize=True,resizable=False)

window.bring_to_front()
window.Maximize()
window.bind("<Escape>", "-ESCAPE-")
window.bind("<Return>", "-ENTER-")

window['-VID_OUT-'].expand(True, True)

inst = vlc.Instance()
list_player = inst.media_list_player_new()

media_list = inst.media_list_new([])
list_player.set_media_list(media_list)
player = list_player.get_media_player()

if PLATFORM.startswith('linux'):
    player.set_xwindow(window['-VID_OUT-'].Widget.winfo_id())
else:
    player.set_hwnd(window['-VID_OUT-'].Widget.winfo_id())

media_list.add_media(movie)
list_player.set_media_list(media_list)
list_player.play()
while True:
    event, values = window.read(timeout=1000)

    if event == sg.WIN_CLOSED:
        break
    if event == '-ENTER-':
        list_player.play()
    if event == '-ESCAPE-':
        list_player.stop()
        window.close()
window.close()

I’ve converted some of my Vuze media to the correct resolution using kdenlive.

I’ve added a new profile. 5760×1080 dont forget to change the display ratio!

Kdenlive howto

NFO creator with poster art generator

Created this for usage with Kodi, for our own personal movies, which can’t be scraped by movie scrapers obviously.
(dvd-rip,digital video, mobile movies, OBS, Vuze and Nikon movies for example)

UPDATE V2 added functions

This is a followup on:

Works on Windows also!

Needed libraries: (install with pip)
pathlib, Pillow, pymediainfo,PySimpleGUI,python-vlc

NFO created

Title – editable (generated from filename)
Duration – mediainfo data in seconds
Plot – Single line type it yourself
Actors – Predefined
Tags – Predefined checkboxes
Country – Pulldown ( I use this as location, Scotland, Asia, Garden)
Year – default 2023, copy button from mobile phone metadata year
Genre – Pulldown

Works

  • File select and loaded
  • Play movie, scrub thretro movie
  • Create snapshot
  • Play/Mute/Pause
  • Playtimer
  • Auto update NFO text field with selectable options
  • Poster art – rotate
  • Poster art – brightness
  • Reload Thumb after snapshotting
  • Year extracted from filename
  • add tag field
  • Next file (button AND functionality)
  • checkboxes from text list
  • Read NFO from file into fields
  • Poster art – aspect ratio

Needs work

Wish list

  • Poster art – contrast?
  • Low – more mediainfo?
  • Low – Media rotate??

The GUI in action (V1)

Kodi example

CODE ( WIP ) V2
I place this python script in every library directory. So i can change the checkboxes and the NFO being generated.
I fill the directory with symlinks for every movie I want to have included in this directory, but this is not needed. (thats the way I like to do things)
(This is the directory I scrape into Kodi)

from pathlib import Path
from sys import platform as PLATFORM
import os
import re
import PySimpleGUI as sg
import vlc
from pymediainfo import MediaInfo
from PIL import Image, ImageEnhance, ImageTk, ImageOps, ImageFilter
from xml.etree import ElementTree as ET


'''
WARNING, NFO overwrites previous!!



'''

alltags = ["music", "pipes", "computer","bbq","fun","art","travel","hobby","family","retro","compilation","retro","vuze"]

#------- WINDOWS FIX --------#
if PLATFORM.startswith('linux'):
    print("yeah linux baby!")
else:
    os.add_dll_directory(r'E:\VLC')

duration=0
tmptag=""

def nextFile(currentfile,dir):
        newfile=""
        dirpath = os.path.dirname(dir)
        fileList = []
        for f in os.listdir(dirpath):
            #fpath = os.path.join(dirpath, f)
            fpath = f
            if os.path.isfile(fpath) and f.endswith(('.mp4', '.mov', '.mpg','.avi','.mkv','.3gp')):
                fileList.append(fpath)
        fileList.sort()
        for i in range(len(fileList)):
#            print("fileList[i] " + fileList[i])
            try:
                if (fileList[i]) == currentfile:
                    newfile=fileList[i+1]
                    break
            except:
                newfile=fileList
        return newfile

def loadthumb(thumbfile):
    # IF exists
    path_to_file = thumbfile
    path = Path(path_to_file)

    if path.is_file():
        im = Image.open(thumbfile)
        im=ImageOps.contain(im, (640,640)) 
        thumbimage = ImageTk.PhotoImage(image=im)
        window['thumb'].update(data=thumbimage)
    else:
        window['thumb'].update("")

def loadnfo(file):

    #LOAD NFO
    nfo=file + ".nfo"
    path = Path(nfo)

    if path.is_file():
            f = open(nfo, "r")
            innfo=f.read()
            f.close()
#            print ("file : " + nfo)
            innfo="".join([s for s in innfo.strip().splitlines(True) if s.strip()])

# Clear some fields
            window['year'].update("")
            window['plot'].update("")

            # Update fields test
            if ET.fromstring(innfo).find('country') is not None:
                nfolocation = ET.fromstring(innfo).find('country')
                window['location'].update(value=nfolocation.text)
            if ET.fromstring(innfo).find('year') is not None:
                nfoyear = ET.fromstring(innfo).find('year')
                window['year'].update(nfoyear.text)
            if ET.fromstring(innfo).find('plot') is not None:
                nfoplot = ET.fromstring(innfo).find('plot')
                window['plot'].update(nfoplot.text)
            if ET.fromstring(innfo).find('title') is not None:
                nfotitle = ET.fromstring(innfo).find('title')
                window['-NM-'].update(nfotitle.text)
            if ET.fromstring(innfo).find('genre') is not None:
                nfogenre = ET.fromstring(innfo).find('genre')
                window['genre'].update(value=nfogenre.text)

            window['coline'].update(False)
            window['henri'].update(False)
            window['monique'].update(False)
            for actor in ET.fromstring(innfo).findall('actor'):
                name = actor.find('role').text
                if name == "Coline":
                    window['coline'].update(True)
                if name == "Henri":
                    window['henri'].update(True)
                if name == "Monique":
                    window['monique'].update(True)
            for alltag in alltags:
                window[alltag].update(False)

            if ET.fromstring(innfo).find('tag') is not None:
                for tag in ET.fromstring(innfo).findall('tag'):
                    if tag.text in window.AllKeysDict:
                        window[tag.text].update(True)

            window['coline'].update(False)
            window['henri'].update(False)
            window['monique'].update(False)
            for actor in ET.fromstring(innfo).findall('actor'):
                name = actor.find('role').text
                if name == "Coline":
                    window['coline'].update(True)
                if name == "Henri":
                    window['henri'].update(True)
                if name == "Monique":
                    window['monique'].update(True)
            for alltag in alltags:
                window[alltag].update(False)

            if ET.fromstring(innfo).find('tag') is not None:
                for tag in ET.fromstring(innfo).find('tag'):
                    window[tag.text].update(True)

    else:
        innfo=""
    window['LOADNFO'].update(innfo)





#------- Button definition --------#
def btn(name):  
    return sg.Button(name, size=(6, 2), pad=(1, 1))

#def tag(name):  
#    return sg.Checkbox(name, enable_events=True)

#------- GUI definition & setup --------#
sg.theme('SystemDefaultForReal')


l1=sg.Text("New title")
l2=sg.Multiline(" ", expand_x=True, key='-OUT-', expand_y=True,justification='left', size=(20,15))
l3=sg.Multiline(" ", expand_x=True, key='LOADNFO', expand_y=True,justification='left', size=(20,15))
l4=sg.Text("Plot")
t1=sg.Input("", key='-NM-')
t2=sg.Input("", key='plot',enable_events=True)
cb=[]

for alltag in alltags:
    cb.append(sg.Checkbox(alltag, key=alltag, enable_events=True))




cb.append(sg.Input("", key='addtag',size=(5, 1), enable_events=True))
cb.append(sg.Combo(["Lab","Datacenter","home","Outside","Outside","Lloydwebber","Steenweg","Enschede","Netherlands","Germany","Italy","Scotland","Canada","Egypt","Belgium","Sweden","Ireland","Asia","NewZealand","Hilversum"],default_value='Lab',key='location'))
cb.append(sg.Combo(["Storage","Funny","Vacation","Music","Relation"],default_value='Storage',key='genre'))
nameyear=[]
nameyear.append(sg.Checkbox("Coline", key='coline', enable_events=True,default=True))
nameyear.append(sg.Checkbox("Henri", key='henri', enable_events=True,default=True))
nameyear.append(sg.Checkbox("Monique", key='monique', enable_events=True,default=False))
nameyear.append(sg.Input("2023",key='year',size=(5, 1), enable_events=True))
nameyear.append(sg.Button("copyyear\nmetadata", key='copymeta', size=(20, 1), pad=(1, 1), enable_events=True))
nameyear.append(sg.Input("",key='metayear',size=(5, 1), enable_events=True))
nameyear.append(sg.Button("copyyear\nfilename", key='copyname', size=(20, 1), pad=(1, 1), enable_events=True))
nameyear.append(sg.Input("",key='fileyear',size=(5, 1), enable_events=True))
b1=sg.Button("WRITE NFO", key='write', enable_events=True)
b2=sg.Button("Exit", key='exit', enable_events=True)

#------- Layout total --------#

layout = [[sg.Input(key='-VIDEO_LOCATION-', visible=False, enable_events=True),
          sg.FileBrowse(file_types=(("Video files", "*.mkv *.mov *.mp4 *3gp *avi *mpg"),)),btn('next')],
          [sg.Text('Load media to start', key='-MESSAGE_AREA-')],
          [sg.Image('', size=(600, 480), key='-VID_OUT-', pad=(1, 1)),sg.Image('', size=(600, 480), key='thumb', pad=(1, 1))],
          [btn('mute'), btn('play'), btn('pause'), btn('stop'), btn('snap'), btn('snaprot'), btn('lighten'), btn('darken'), sg.Combo(["4:3","16:9","9:16"],key='aspect',enable_events=True)],
          [sg.Slider(range=(0, 1000), default_value=1, expand_x=True, enable_events=True, orientation='horizontal', key='-SL-')],
          [sg.Text('org title', key='orgtitle')],
          [l1, t1, l4, t2],[cb],[nameyear],[b1, l2, l3, b2],
          [sg.Text('GENERATED                            -= mini nfo writer =-                              PREVIOUS', key='footer')]
          ]

#------- Set window --------#
window = sg.Window('Mini NFO generator', layout, element_justification='center', finalize=True, resizable=True)
#------- VID out window --------#
window['-VID_OUT-'].expand(True, True)               
#------------ Media Player Setup ---------#
inst = vlc.Instance()
list_player = inst.media_list_player_new()
media_list = inst.media_list_new([])
list_player.set_media_list(media_list)
player = list_player.get_media_player()
if PLATFORM.startswith('linux'):
    player.set_xwindow(window['-VID_OUT-'].Widget.winfo_id())
else:
    player.set_hwnd(window['-VID_OUT-'].Widget.winfo_id())

#------------ I Want default mute ---------#
player.audio_set_mute(True)

#------------ The Event Loop ------------#
while True:
    event, values = window.read(timeout=1000)       # run with a timeout so that current location can be updated
    if event == sg.WIN_CLOSED:
        break



    if event == 'mute':
        mutevalue = player.audio_get_mute()
        if mutevalue==0:
            player.audio_set_mute(True)
        if mutevalue==1:
            player.audio_set_mute(False)
    if event == 'play':
        list_player.play()
    if event == 'pause':
        list_player.pause()
    if event == 'stop':
        list_player.stop()
    if event == 'snap':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        player.video_take_snapshot(0, newname, 0, 0)
        loadthumb(newname)
    if event == 'snaprot':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = im1.rotate(90, Image.NEAREST, expand = 1)
        im1 = im1.save(newname)
        loadthumb(newname)

    if event == 'lighten':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = ImageEnhance.Brightness(im1)
        im1 = im1.enhance(1.2)
        im1 = im1.save(newname)
        loadthumb(newname)
        
    if event == 'darken':
        list_player.pause()
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        im1 = Image.open(newname)
        im1 = ImageEnhance.Brightness(im1)
        im1 = im1.enhance(0.8)
        im1 = im1.save(newname)
        loadthumb(newname)

    if event == 'aspect':
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + "-poster.png"
        if values['aspect'] == "4:3":
            im1 = Image.open(newname)
            im1 = im1.resize((1920 ,int(1920/4*3)), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)
        if values['aspect'] == "16:9":
            im1 = Image.open(newname)
            im1 = im1.resize((1920 ,int(1920/16*9)), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)
        if values['aspect'] == "9:16":
            im1 = Image.open(newname)
            im1 = im1.resize((int(1920/16*9),1080), Image.ANTIALIAS)
            im1 = im1.save(newname)
            loadthumb(newname)


    if event == 'write':
        filename = values['-VIDEO_LOCATION-']
        shortfilename = filename.rsplit( ".", 1 )[ 0 ] 
        newname = shortfilename + ".nfo"
        f = open(newname, "w")
        f.write(out)
        f.close()
        loadnfo(shortfilename)

    if event == 'copymeta':
        if datefromfile is not None:
            window['year'].update(datefromfile)

    if event == 'copyname':
        if fileyear is not None:
            window['year'].update(fileyear)


    if event == '-SL-':
        pos=int(values['-SL-'])
        player.set_position(pos/ 1000.0)

        
    if values['coline'] == False:
         colinetext = ""
    if values['coline'] == True:
        colinetext = """<actor>
  <name>Coline Lastname</name>
  <role>Coline</role>
</actor>"""
    if values['henri'] == False:
         henritext = ""
    if values['henri'] == True:
        henritext = """<actor>
  <name>Henri Aanstoot</name>
  <role>Henri</role>
</actor>"""
    if values['monique'] == False:
            moniquetext = ""
    if values['monique'] == True:
        moniquetext = """<actor>
  <name>Monique Lastname</name>
  <role>Monique</role>
</actor>"""

        
    if event == '-VIDEO_LOCATION-':
        if values['-VIDEO_LOCATION-'] and not 'Video URL' in values['-VIDEO_LOCATION-']:
            media_list.remove_index(0)
            media_list.add_media(values['-VIDEO_LOCATION-'])
            list_player.set_media_list(media_list)
            shortname = values['-VIDEO_LOCATION-']
            shortname = shortname.rsplit( "/", 1 )[ 1 ] 
            nextfile=nextFile(shortname,"./")
            shortname = shortname.rsplit( ".", 1 )[ 0 ] 
            window['-NM-'].update(shortname) # only add a legit submit
            list_player.stop()
            list_player.next()
            thumbname=shortname + "-poster.png"
            loadthumb(thumbname)

            #Print org title
            window['orgtitle'].update(values['-VIDEO_LOCATION-'])


            loadnfo(shortname)


            #DURATION
            mi = MediaInfo.parse(values['-VIDEO_LOCATION-'])
            duration=int(mi.tracks[0].duration/1000)
            
            #DATE FROM media
            metadate=mi.tracks[0].encoded_date
            if metadate is not None:
                datefromfile=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", metadate)), None)
                window['metayear'].update(datefromfile)

            # YEAR FROM FILENAME 
            fileyear=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", shortname)), None)
            # year from filename
            if fileyear is not None:
                window['fileyear'].update(fileyear)


    if event == 'next':
        
        values['-VIDEO_LOCATION-'] = nextfile
        window['-VIDEO_LOCATION-'].update(nextfile)

        if values['-VIDEO_LOCATION-'] and not 'Video URL' in values['-VIDEO_LOCATION-']:
            media_list.remove_index(0)
            media_list.add_media(values['-VIDEO_LOCATION-'])
            list_player.set_media_list(media_list)
            shortname = values['-VIDEO_LOCATION-']
 #           shortname = shortname.rsplit( "/", 1 )[ 1 ] 
            nextfile=nextFile(shortname,"./")
            #print(nextfile)
            shortname = shortname.rsplit( ".", 1 )[ 0 ] 
            window['-NM-'].update(shortname) # only add a legit submit
            list_player.stop()
            list_player.next()
            thumbname=shortname + "-poster.png"
            loadthumb(thumbname)

            #Print org title
            window['orgtitle'].update(values['-VIDEO_LOCATION-'])
            

            loadnfo(shortname)

            #DURATION
            mi = MediaInfo.parse(values['-VIDEO_LOCATION-'])
            duration=int(mi.tracks[0].duration/1000)
            
            #DATE FROM media
            metadate=mi.tracks[0].encoded_date
            if metadate is not None:
                datefromfile=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", metadate)), None)
                window['metayear'].update(datefromfile)

            # YEAR FROM FILENAME 
            fileyear=next(iter(re.findall(r"[1][9][8-9][0-9]|[2][0][0-9]{2}", shortname)), None)
            # year from filename
            if fileyear is not None:
                window['fileyear'].update(fileyear)
#        media_list.add_media(nextfile)
##        shortname = nextfile.rsplit( ".", 1 )[ 0 ] 
##        window['-NM-'].update(shortname) # only add a legit submit
#        print(nextfile)
#        window['-VIDEO_LOCATION-'].update(nextfile)
#        list_player.next()
# hier moeten wat update dingen bij!




    # Add tag
    if event == 'addtag':
        tmptag="<tag>" + values['addtag'] + "</tag>"

#------------ exit ---------#

    if event=='exit':
         exit()
#------------ TAGS ---------#
    
    subs=[x.Text for x in cb if x.get()==True]
    for idx, x in enumerate(subs):
        subs[idx]="<tag>" + subs[idx] + "</tag>"


    if values['year'] == False:
        year=str(2023)
    else:
        year=values['year']
    if values['location'] == False:
        location="Lab"
    else:
        location=values['location']
    if values['plot'] == False:
        plot=""
    else:
        plot=values['plot']
    if values['genre'] == False:
        genre="Storage"
    else:
        genre=values['genre']
#------------ NFO TEXT FIELD ---------#

    out="""<movie>
<title>{}</title>
<plot>{}</plot>
<genre>{}</genre>
<duration>{}</duration>
<year>{}</year>
<country>{}</country>
{}
{}
{}
{}
{}
</movie>
""".format(values['-NM-'],plot,genre,duration,year,location,colinetext,henritext,moniquetext,tmptag, "\n".join(subs))
    out="".join([s for s in out.strip().splitlines(True) if s.strip()])
    window['-OUT-'].update(out)

#------------ Messages and timer ---------#

    # update elapsed time if there is a video loaded and the player is playing
    if player.is_playing():
        window['-MESSAGE_AREA-'].update("{:02d}:{:02d} / {:02d}:{:02d}".format(*divmod(player.get_time()//1000, 60),
                                                                     *divmod(player.get_length()//1000, 60)))
    else:
        window['-MESSAGE_AREA-'].update('Load media to start' if media_list.count() == 0 else 'Ready to play media' )

window.close()

Python pysimplegui with VLC example

A work in progress – see followup

pip3 install python-vlc && pip3 install PySimpleGUI

Kodi movie NFO file editor test

And a player in python

Some example code

from pathlib import Path

import PySimpleGUI as sg
import vlc
from sys import platform as PLATFORM


sg.theme('DarkBlue')

def btn(name):  
    return sg.Button(name, size=(6, 1), pad=(1, 1))

b1=sg.Button("WRITE NFO")
b2=sg.Button("Exit")

layout = [[sg.Input(key='-VIDEO_LOCATION-', visible=False, enable_events=True),
          sg.FileBrowse(file_types=(("MP4 Files", "*.mp4"),))],
          [sg.Image('', size=(600, 480), key='-VID_OUT-')],
          [btn('mute'), btn('play'), btn('skip 5sec'), btn('pause'), btn('stop'), btn('snap')],
          [sg.Text('Load media to start', key='-MESSAGE_AREA-')],
          [sg.Slider(range=(0, 100), default_value=5, expand_x=True, enable_events=True, orientation='horizontal', key='-SL-')],
          [[b1, b2]]]

window = sg.Window('Mini NFO generator', layout, element_justification='center', finalize=True, resizable=True)

window['-VID_OUT-'].expand(True, True)                

inst = vlc.Instance()
list_player = inst.media_list_player_new()
media_list = inst.media_list_new([])
list_player.set_media_list(media_list)
player = list_player.get_media_player()
if PLATFORM.startswith('linux'):
    player.set_xwindow(window['-VID_OUT-'].Widget.winfo_id())
else:
    player.set_hwnd(window['-VID_OUT-'].Widget.winfo_id())

player.audio_set_mute(True)
while True:
    event, values = window.read(timeout=1000)       
    if event == sg.WIN_CLOSED:
        break

    if event == 'play':
        list_player.play()
    if event == 'pause':
        list_player.pause()
    if event == 'stop':
        list_player.stop()
    if event == 'snap':
        list_player.pause()
        newname = values['-VIDEO_LOCATION-'] + ".png"
        player.video_take_snapshot(0, newname, 400, 300)
    if event == 'next':
        player.set_position(0.5)
        list_player.play()
    if event == 'mute':
        player.audio_set_mute(True)
    if event == '-SL-':
        player.set_position(int(values['-SL-']/100))
    if event == '-VIDEO_LOCATION-':
        if values['-VIDEO_LOCATION-'] and not 'Video URL' in values['-VIDEO_LOCATION-']:
            media_list.add_media(values['-VIDEO_LOCATION-'])
            list_player.set_media_list(media_list)
            window['-VIDEO_LOCATION-'].update('Video URL or Local Path:') 
            list_player.next()

    if player.is_playing():
        window['-MESSAGE_AREA-'].update("{:02d}:{:02d} / {:02d}:{:02d}".format(*divmod(player.get_time()//1000, 60),
                                                                     *divmod(player.get_length()//1000, 60)))
    else:
        window['-MESSAGE_AREA-'].update('Load media to start' if media_list.count() == 0 else 'Ready to play media' )

window.close()

My dosbox assembly directory

Below you can find the files in a zip for writing assemby machine code in dosbox.

I’ve got mine extracted in ~/projects/dos

To automount this i’ve edited

~/.dosbox/dosbox-*.conf
;-------- bottom part
[autoexec]
# Lines in this section will be run at startup.
# You can put your MOUNT lines here.

mount c: /home/myusername/projects/dos
c:

The files

  • A.BAT – Runs editor, masm,linker (start with a<space>progname
  • DEBUG.COM – msdos debugger
  • EXE2BIN.EXE – exe to com (if segments allow)
  • GFX.ASM – example template (see below)
  • LINK.EXE – masm linker
  • MASM.EXE – masm compiler
  • Q* – editor stuff
  • Q.EXE – editor
  • SR.EXE – sourcer
  • SYMDEB.EXE
  • TASM.EXE – turbo assembler
  • TD.EXE – turbo debugger
  • TLINK.EXE – turbo linker

Template

; everything in 64k CS, DS, and SS are the same
.model small
; start pointer
.stack 100h
.code
start:
	; set mode 13 (320x200 * 265 colors palette)
    mov ah,0	
    mov al,13h
    int 10h

    ; set pixel in the middle color 2 (cyan)
    mov ah,0ch
    mov al,2
    mov cx,160
    mov dx,100
    int 10h

	; wait key input
    mov ah,0
    int 16h

	; set mode back to text
    mov ah,0
    mov al,3
    int 10h

	; exit to dos
    mov ax,4c00h
    int 21h
end start

Usage

start dosbox
a gfx (edit gfx.asm example)
esc, q, e
gfx (run program)

8086 sideway scroller ‘n stuff

Today I was working on my own brew ISA card (wirewrapping).
Did some mini modeling stuff.
Sorted some pipetunes.
And played around with my 8086.

Got it on a desk now, and replaced the harddisk with the CF card.
Also got an old SoundBlaster working, so i wanted to see what of my old code could still run.
Apparanty most code was compiled for 386/486 🙁
So i recompiled some stuff.
Below a horizontal scroller example.

Meanwhile i got my new fans in for my NUC (Kodi player, it was making a hell of a noise due to bad ball bearings.

New Roms (eeprom)

Got some new roms in.
These are for my 8088/8086 the 6502 computer and C64 cartridges.

While I seldom had any problem writing to these, now I could not write one!
Erasing didn´t give me an error?!?

henri@zspot:~/projects/wozmon8088/mon8086$ minipro -w mon8086.rom -p AT28C64
Found TL866II+ 04.2.129 (0x281)
Warning: Firmware is newer than expected.
  Expected  04.2.128 (0x280)
  Found     04.2.129 (0x281)
Erasing... 0.02Sec OK
Writing Code...  9.57Sec  OK
Reading Code...  0.12Sec  OK
Verification failed at address 0x0001: File=0xAA, Device=0xFF

Whenever you get this, check the markings of the chip!

Mine are AT28C64b !!!!!!!!!!!

Change your command accordingly.
Another thing to watchout for is write protect, look at the commands

minipro -l | grep 28C64 
Found TL866II+ 04.2.129 (0x281)
Warning: Firmware is newer than expected.
  Expected  04.2.128 (0x280)
  Found     04.2.129 (0x281)
AM28C64A@DIP28
AM28C64A@PLCC32
AM28C64A@SOIC28
AM28C64AE@DIP28
AM28C64AE@PLCC32
AM28C64AE@SOIC28
AM28C64B@DIP28
AM28C64B@PLCC32
AM28C64B@SOIC28
AM28C64BE@DIP28
AM28C64BE@PLCC32
AM28C64BE@SOIC28
AT28C64
AT28C64@PLCC32
AT28C64@SOIC28
AT28C64B
AT28C64B@PLCC32
AT28C64B@SOIC28
AT28C64E
AT28C64E@PLCC32
AT28C64E@SOIC28
AT28C64F
AT28C64F@PLCC32
AT28C64F@SOIC28
CAT28C64A
CAT28C64A@PLCC32
CAT28C64A@SOIC28
CAT28C64B
CAT28C64B@PLCC32
CAT28C64B@SOIC28
XLE28C64A
XLE28C64A@PLCC32
XLE28C64B
XLE28C64B@PLCC32
XLE28C64B@SOIC28
XLS28C64A
XLS28C64A@PLCC32
XLS28C64B
XLS28C64B@PLCC32
XLS28C64B@SOIC28
28C64A
28C64A@PLCC32
28C64A@SOIC28
28C64AF
28C64AF@PLCC32
28C64AF@SOIC28
28C64B
28C64B@PLCC32
28C64B@SOIC28
UPD28C64
UPD28C64@SOIC28
KM28C64A
KM28C64A@PLCC32
M28C64
M28C64@PLCC32
M28C64@SOIC28
M28C64A
M28C64A@PLCC32
M28C64A@SOIC28
M28C64-xxW
M28C64-xxW@PLCC32
M28C64-xxW@SOIC28
M28C64
M28C64@PLCC32
M28C64@SOIC28
M28C64A
M28C64A@PLCC32
M28C64A@SOIC28
M28C64-xxW
M28C64-xxW@PLCC32
M28C64-xxW@SOIC28
X28C64

Compilers (mainly machinecode)

A compiler is a program that converts instructions into a machine-code or lower-level form so that they can be read and executed by a computer.

Below are some machinecode compilers i’ve used.

FOR X86

Masm – Used this in Msdos
Need linker (link), can use macro’s
Most of my old stuff was written using masm

Tasm – Used this in Msdos
Need linker (tlink)
Some of my old stuff was written using Tasm (our boot demo was)

Fasm – Used this to compile for 8086/8088 under linux
Does not need linker, Open Source, Fast!, Written in fasm

Nasm – Used this for some machinecode under linux
Cross platform, can use Macro’s, Open Source

FOR 65xx

Acme – Multi-platform cross assembler for 6502/6510/65816 CPU
Used for my DIY 6502

CC65 – cc65 – complete cross development package for 65(C)02 systems
Used for my DIY 6502

Kickassembler – Java but works okay

Other compilers are for example the Arduino IDE or PlatformIO, which comes with AVR compilers. (Mostly C based)

FOR AMIGA

Seka – assembler

And a really old skool one i’ve used : PL/M-86 Compiler

I’m not sure i’ve used compilers for 8085/680x