Saturday, 14 December 2019

LPT2MIDI

Arduino based LPT2MIDI device for my retro PC...


Arduino code:

void receive() {
  Serial.write((PIND>>3)+(PINB<<5)&255);
}

void setup() {
  DDRB = 0;
  DDRD = 0;
  Serial.begin(31250); // MIDI uses 31.25 kbps serial
  attachInterrupt(digitalPinToInterrupt(2), receive, RISING);
}

If you want to increase the serial buffer size, then change the parameter SERIAL_TX_BUFFER_SIZE in HardwareSerial.h to your liking. This arrangement has the added benefit that if the MIDI messages are arriving too fast at certain point in time, but not others, then Arduino buffers them and one may avoid some issues that would otherwise be present (although LPT isn't particularly fast port so the benefit might not be that big).

You can trap port operations on 386 or higher using the following code in DOS. Requires EMM386 (loadhigh works). This version is rather simplistic and doesn't implement all the functions such as those of port 331h so it only works in some cases (like my custom Supaplex hack below) where basic writes to 330h are sufficient. However, it should be fairly easy to implement the missing functions or just add this device to existing project like SOFTMPU.

nasm -fbin trapmidi.asm -o trapmidi.com

        org     100h

        mov     ax, 4a15h        ; EMM386 I/O trap
        mov     bx, 0
        mov     dx, 330h
        shl     edx, 16
        mov     dx, 330h
        mov     cx, 1
        mov     si, io_dispatch_table
        mov     di, end
        int     2fh

        mov     ax, 0x3100
        mov     dx, 512/16
        int     21h

align 16
handler330:
        mov     ah, al        

        mov     al, 255
        mov     dx, 0x37a        ; 1st parallel port status register
        out     dx, al
        times 4 in al, dx

        mov     al, ah
        mov     dx, 0x378        ; 1st parallel port data register
        out     dx, al
        times 4 in al, dx

        xor     al, al
        mov     dx, 0x37a        ; status register is used to signal
        out     dx, al           ; port operation on rising edge
        times 8 in al, dx

        retf

align 16
io_dispatch_table:
        dw      0x0330
        dw      handler330
end:

While we're at it, let's hack away a few bugs in roland.snd of my favorite DOS era game, Supaplex.

nasm -fbin roland.asm -o sp_org\roland.snd

incbin "roland.snd", $, 0x0085-$   ; remove port delays
retn

incbin "roland.snd", $, 0x0098-$   ; remove port delays
mov     dx, 330h
mov     al, ah
out     dx, al
retn

incbin "roland.snd", $, 0x0189-$   ; remove volume op (causes clipping)
times 3 nop

incbin "roland.snd", $, 0x018e-$   ; remove volume op (causes clipping)
times 3 nop

incbin "roland.snd", $, 0x0193-$   ; remove volume op (causes clipping)
times 3 nop

incbin "roland.snd", $, 3968

Sound Blaster output is also not working right (on my particular setup) so let's rewrite blaster.snd.

nasm -fbin blaster.asm -o sp_org\blaster.snd

%macro waitdsp 0
%%wait:
in      al, dx
or      al, al
js      %%wait
%endmacro

push    ax
push    bx
push    cx
push    dx

cmp     ax, 0
jne     skip0
mov     cx, explosion
mov     di, infotron - explosion - 1
jmp     short start
skip0:
cmp     ax, 1
jne     skip1
mov     cx, infotron
mov     di, push - infotron - 1
jmp     short start
skip1:
cmp     ax, 2
jne     skip2
mov     cx, push
mov     di, zonk - push - 1
jmp     short start
skip2:
cmp     ax, 3
jne     skip3
mov     cx, zonk
mov     di, bug - zonk - 1
jmp     short start
skip3:
cmp     ax, 4
jne     skip4
mov     cx, bug
mov     di, base - bug - 1
jmp     short start
skip4:
cmp     ax, 5
jne     skip5
mov     cx, base
mov     di, exit - base - 1
jmp     short start
skip5:
cmp     ax, 6
jne     skip6
mov     cx, exit
mov     di, end - exit - 1
jmp     short start
skip6:
jmp     skip
start:
mov     dx, 0x0a        ; DMA: write  mask register
mov     al, 15;         ; channel 1 disabled
out     dx, al

mov     dx, 0x0c        ; DMA: clear byte pointer flip-flop
mov     al, 0
out     dx, al

mov     dx, 0x0b
mov     al, 0x49        ; single-cycle playback on channel 1
out     dx, al

mov     bx, cs
shl     bx, 4
add     bx, cx          ; offset

mov     dx, 0x02        ; DMA: channel 1 address
mov     al, bl
out     dx, al
mov     al, bh
out     dx, al

mov     ax, cs
mov     bx, cx
shr     bx, 4
add     bx, ax
shr     bx, 12          ; DMA: page in bx

mov     dx, 0x83        ; DMA: channel 1 page
mov     al, bl
out     dx, al

mov     bx, di
mov     dx, 0x03        ; DMA channel 1 count
mov     al, bl
out     dx, al
mov     al, bh
out     dx, al

mov     dx, 0x0a
mov     al, 1           ; DMA 1 channel enabled
out     dx, al

mov     dx, 0x22c       ; sound blaster (A220) DSP write data
waitdsp
mov     al, 0x40        ; sample rate
out     dx, al          ; SB

waitdsp
mov     al, 256 - 1000000/8333
out     dx, al          ; SB

waitdsp
mov     al, 0x14        ; 8-bit PCM output
out     dx, al          ; SB

waitdsp
mov     al, bl          ; lo(size)
out     dx, al          ; SB

waitdsp
mov     al, bh          ; hi(size)
out     dx, al          ; SB
skip:
pop     dx
pop     cx
pop     bx
pop     ax
iret
state:
db      0
explosion:
incbin "explode.raw"
infotron:
incbin "infotron.raw"
push:
incbin "push.raw"
zonk:
incbin "zonk.raw"
bug:
incbin "bug.raw"
base:
incbin "base.raw"
exit:
incbin "exit.raw"
end:

We also need to make changes to the main binary and while we're at it, let's convert it to a .COM just for kicks.

nasm -fbin supaplex.asm -o sp_org\supaplex.com

        org     100h

        mov     dx, 0x226                              ; reset sound blaster
        mov     al, 1
        out     dx, al

        sub     al, al
delay:
        dec     al
        jnz     delay
        out     dx, al
        sub     cx, cx
empty:
        mov     dx, 0x22e
        in      al, dx
        or      al, al
        jns     nextattempt
        sub     dl, 4
        in      al, dx
        cmp     al, 0xaa
        je      resetok
nextattempt:
        loop    empty
resetok:
        mov     dx, 0x22c                              ; enable sound blaster dac
wait2:
        in      al, dx
        and     al, 0x80
        jnz     wait2
        mov     al, 0xd1
        out     dx, al

        mov     ax, cs
        add     ax, 0x58d4                             ; initial ss
        add     ax, (512+0x0100)/16                    ; skip org and loader
        mov     ss, ax
        mov     sp, 0x0080                             ; initial sp

        mov     ax, cs
        add     ax, 0x0aff                             ; initial cs
        add     ax, (512+0x0100)/16                    ; skip org and loader
        push    ax
        mov     ax, 0x0010                             ; initial ip
        push    ax
        retf                                           ; jumps to start

        times 512-($-$$) nop

        incbin "supaplex\supaplex.exe", $, 0x0526-$      ; 50:70 timing
        db      0xa0, 0x92, 0x0d ; mov al, [0xd92]
        inc     al
        and     al, 7
        db      0xa2, 0x92, 0x0d ; mov [0xd92], al
        cmp     al, 3
        db      0x74, 0x6d       ; je 0x5a1
        cmp     al, 0
        db      0x74, 0x69       ; je 0x5a1
        nop
        nop

        incbin "supaplex\supaplex.exe", $, 0x05c3-$      ; no PIT
        db 0xb0, 0x70 ; mov al, 0x70
        int     0x21
        times 12 nop

        incbin "supaplex\supaplex.exe", $, 0x55a5-$      ; remove mouse
        retn

        incbin "supaplex\supaplex.exe", $, 0x5632-$      ; use menu with backspace
        db 0x80, 0x3e, 0x7b, 0x16, 0x01

        incbin "supaplex\supaplex.exe", $, 0x56E2-$
        in      al, dx
        test    al, 0x8
        db      0x74, 0xed
        mov     dx, 0x03c0
        mov     al, 0x33
        out     dx, al
        db      0xA0, 0x96, 0x0D
        out     dx, al
        int     0x70
        pop     ax
        pop     dx
        ret

        incbin "supaplex\supaplex.exe", $, 0x5b47-$      ; blaster.snd filesize
        mov     cx, 36123

        incbin "supaplex\supaplex.exe", $, 0x5b7a-$      ; blaster.snd filesize
        mov     cx, 36123

        incbin "supaplex\supaplex.exe", $, 0x8970-$      ; crack
        db      0

        incbin "supaplex\supaplex.exe", $, 45948-$

MOVING.DAT also has some minor imperfections in graphics, but let's fix those later...

A few other observations related to Supaplex include:

1) Supaplex needs at least 66 MHz 486 to run 70 Hz without minor tearing. Part of the reason why that much CPU power is needed is that the graphics seem to be drawn in reverse order so there's relatively speaking less time before vsync is to occur. Scrolling is "independent" so it may not tear even if sprite animation does (for example on MiSTer).

2) Music playback seems to cause minor video jerking no matter how fast a CPU (occasional duplicated frame). This jerking does not appear on the FPGA (ao486 and MiSTer FPGA) so it's probably caused by slow port operations screwing with the vertical sync (on the FPGA the port operations are much faster than on a real PC).

3) Unfortunately the FPGA is otherwise a bit too slow (minor tearing) and has some minor scrolling bug in the VGA implementation (right edge of the screen is missing varying number of pixels while scrolling). It would be nice to fix these two issues with ao486 on the FPGA at some. Proper roland CM-32L emulation would also be nice so one wouldn't need to plug in a real module (MUNT at the moment is just unusable because it's too laggy).

4) One can remove port in-based delay and the jerking is gone on the PC (works with the FPGA), but so is the music because adlib and roland are too slow and require their port delay so one needs some kind of buffering. Unfortunately my original idea of using the parallel port + arduino to do this buffering didn't quite work because the parallel port is also not quite fast enough (it helps a little compared to straight roland or adlib, but not enough).

5) I also tried rewriting the whole music routine using faster interrupts (64x 50 Hz) to get rid of port delays and use counters instead and while this worked to some degree, it causes some other timing issues with the main code which are a bit difficult to fix due to lack of game source code.

6) I also tried running the music routine during the 70 Hz vblank. That works to eliminate the jerking, but also results in the music playing way too fast.

7) Perhaps I just have to roll my own ISA card, although arduino nano doesn't have enough pins for that, at least not without some auxiliary chips, but there are alternatives.

8) I could also write the music routines natively for arduino and have them run independently of PC. That should fix any music related timing issues.

9) Or one could rewrite the whole damn game... but that's a lot of trouble...

On the PC the way to get rid of the jerking by entirely removing Programmable Interval Timer (PIT) and instead do timing with vsync. I've solved the problem of music playing too fast by skipping call to music routine 2 frames out of 7 so it plays at normal rate. SBMIDI calls are still too slow and 6+35 in's for ADLIB are too slow, but on OPL3 (SBPRO+) 1+4 in's are enough so with OPL3 we get away without any jerking on stock hardware using this 70-50 solution and SBMIDI can be replaced with my LPT2MIDI and again we get away without any jerking when dealing with the 70-50 case.

Wednesday, 26 June 2019

Trivializing philosophy is exactly what we ought to do

Your consciousness is that part of the cosmos which correlates perfectly with what it is to experience being you. In the end it doesn't say much more than that you are what you are. It's a trivial tautology. However, all valid logical reasoning is just tautologies anyway and in the end logical conclusions are nothing more than observing the unavoidable.

It is in fact unjustified thought that existence would require a reason. The cosmic wave function may be existence itself and as such it has always existed to the extent that time exists. Its deterministic time evolution among other things corresponds to our everyday experience of movement in time, but time too is just an aspect of existence like any other. We have no examples of things beginning to exist and no examples of things ceasing to exist so there is no argument to be made about the necessity of reasons for existence.


Generally speaking, trivialising philosophy (and all problems) is exactly the goal. This is the way problems are solved. Should there be something one doesn't know which would be necessary to construct a better model, one just records this gap of knowledge waiting to be filled in the future when sufficient data becomes available and stays on track with the best data and models so far until further notice.

There's almost infinite amount of complexity associated with every question imaginable, but if anyone is to get anywhere we must act pragmatically.

Saturday, 1 December 2018

Infinite and eternal

Here I describe an approach to metaphysics based on what I call the principle of metaphysical discrimination. The goal of this principle is to focus on dividing phenomena into clear meaningful distinct categories based on the kinds of limits that can be expected to originate from the continuum between the observed nature of ones own existence and all other existence (further from self). Supernatural postulates like God, being void of predictive utility, are ignored.

We could start by trying to address the question of what is consciousness and how can it exist in a world which seemingly appears to be nothing but a Turing machine (at least to the first order), but that might be waste of time and has been more or less fruitlessly done many times before. Instead, let's start by taking for granted that something we call consciousness exists, we know this because that's us. Whichever way consciousness works, cosmos contains everything that is needed. Let us also note that there exists a kind of limitation we might call a private experience or a qualia which exists for all consciousness we are aware of. This limitation appears to prevent us from knowing what (if anything) it feels like to be someone else.


This sort of limitation resembles a phenomena of complementarity in quantum mechanics which in its simplest form is a limitation on the information any single observer can possess about certain pairs of physical observables such as position and momentum. In this context we can remain agnostic about whether these variables even have an exact simultaneous fundamental existence, and instead simply note that even if they do, the nature of our existence prevents us from ever gaining such knowledge.

The existence of this sort of complementarity in nature leads me to consider the possibility that perhaps private experience is simply another facet of all existence and ultimately no more mysterious than any other observation. Then perhaps postulating that "there is more to things" is just a kind of superstition and ultimately everything simply boils down to observing the kinds of things that exist. One could bring up the experience of time evolution of "now" as another mysterious aspect of consciousness, but time too might be nothing more than another dimension of existence.

If there in fact is nothing more to existence than existence itself (is there really an alternative?) then there can be no reason for existence. Asking for such a reason isn't even coherent, reasons are just correlations in "the set of all existence". In the spirit of Gödel's incompleteness theorems, it probably remains forever impossible to exactly prove such to be the case, but it never the less can be true and the most reasonable conclusion.

Thursday, 22 November 2018

Conservation of etendue

Here's why you can't focus thermal light sources like the sun to a point which would get hotter than the emitting surface of the light source (omnidirectional thermal emitter). This would also violate the laws of thermodynamics. Here's what happens if you try...
...no arrangement of lenses, mirrors or any passive elements allows focusing beyond the surface temperature of the source.
Lenses and parabolic mirrors rather than purely focusing, make an image which has a minimum size that is proportional to the distance of the thermal source and the intensity lost to distance balances the sheets so that focusing alone can't result in hotter spot than the surface of the emitter.

So no matter how large a magnifying glass, even the size of earth, your spot won't get hotter than the surface of the sun. It's just equalization of temperature between two objects assisted by the magnifying glass in a way similar to bringing the objects closer to each other.
However, pure unidirectional emitters like lasers don't exactly have to obey this kind of limit. High temperature corresponds to high entropy and high disorder, whereas in terms of entropy, laser light is highly ordered, but unlike cold systems which are void of energy, laser is a kind of saturated form of energy from which energy can always flow away to increase the entropy of a typical thermodynamic system. Classical thermodynamic systems can't have this sort of state as their entropy can always increase. Only certain well isolated systems can be temporarily driven to this kind of saturated state by external source of energy.

Saturday, 3 November 2018

Small Broadcast Video Monitor for Retrogaming

Retrogaming is best experienced with CRTs. There's least amount of delay and timing and scanline reproduction is authentic. SVGA CRTs designed for 70 Hz would perhaps be the sharpest, but their phosphors are a bit too fast so they flicker with PAL (50 Hz) and NTSC (60 Hz) refresh rates a bit too much in addition to requiring at least line doubling. They don't natively support the TV-era 50/60 Hz 240p modes, but were instead operated in line doubled modes where the common game resolution of 320x200 (70 Hz) was actualy drawn as 320x400. However, some SVGA monitors can be operated at twice the refresh rate at 120 Hz and 240p giving in some ways the best result. This still isn't fully NTSC faithful as there's no way to draw the image without a frame buffer, but it never the less is good for image quality comparisons.

I recently acquired a Broadcast Video Monitor (a kind of high quality TV-style display) so I could authentically and natively view PAL/NTSC signals.

Click to enlarge. Sony BVM-9044D (9" 450 line Trinitron) with analog coaxial RGB input (composite sync). Very clear scanlines. The signal is 240p 60 Hz NTSC. The displayed image has a resolution of 320x200. Horizontal resolution could perhaps be a bit higher, but overall I feel this kind of display would have been perfectly fine for DOS-era games.
Click to enlarge. Nokia 449Xi (15" >1000 line Trinitron) with analog VGA input (RGB). The displayed resolution is single scan 320x200 at 120 Hz.
Click to enlarge. Nokia 449Xi (15" >1000 line Trinitron) with analog VGA input (RGB). The displayed resolution is double scan 320x200 (so 320x400) at 70 Hz (standard mode 13h). Double scan results in very clear and sharp image.

Standard (S)VGA monitors can not be used to faithfully reproduce games such as those of Nintendo Entertainment System due to incompatible signal standards (PAL/NTSC vs. VGA). NTSC having a 15.723 kHz horizontal sync rate vs. VGA 31.46875 kHz. NTSC needs to be either line doubled or vertical scan rate doubled (or both). Doubling the vertical refresh rate would be desirable, but at the same time needs an undesirable frame buffer that adds some delay.


Difference between textmode (80x25) in 240p (60 Hz) with Sony BVM and 400p (70 Hz) SVGA monitor. VGA textmode characters are 9x16 pixels whereas in 240p the characters consist of 8x8 pixels. These correspond to resolutions 640x200 and 720x400.


A few more 240p shots perhaps less commonly from DOS games at 240p.


The monitors.

The TSRs needed for 240p 60 Hz and 120 Hz output in DOS.

Monday, 10 September 2018

Most new ideas are stupid and dangerous not unlike many old ideas, but some of the new ideas are vital and without them we're going to perish and die

The problem with reasons for causal things is that they're related to time, but causality is just an observation. Sticks don't come into existence at their tips. The tips are just an edge (of an object that exists). If the universe exists today, why would it exist tomorrow? What is the cause for the existence of tomorrow? No reason? Perhaps we should try to demonstrate that existence can never have a reason or more exactly that reasons for existence are ultimately nonsense. Then is the reason for the existence of consciousness also nonsense? Perhaps it too is just an observation about a thing that exist.

--

Why not just say that morality and values originate from our state of existence which is fundamentally arbitrary, but can be universal over a broad range of different societies to the extent that we humans share such conditions due to our evolutionary history.

Pareto efficiency is a state of allocation of resources from which it is impossible to reallocate so as to make any one individual or preference criterion better off without making at least one individual or preference criterion worse off. A production-possibility frontier, the red line in the figure above, is an example of a Pareto-efficient frontier, where the frontier and the area left and below it is a continuous room of choices. The red points on the frontier are examples of Pareto-optimal choices of production. Points off the frontier, such as N and K, are not Pareto-efficient, since there exist points on the frontier which Pareto-dominate them [wikipedia].
Suppose evil is defined as ignorance of all the well-being you and others would experience if you behaved another way. In principle dictatorship, free market economy and communism can all be Pareto optimal. Perhaps even societies with slavery. What is Pareto optimal depends on your values so even if evil is ignorance of the landscape of well-being you may well find yourself in favor of dictatorship even if you're well aware of the landscape. Even adding the veil of ignorance won't solve the equation.

--

https://codegolf.stackexchange.com/questions/11880/build-a-working-game-of-tetris-in-conways-game-of-life

Saturday, 1 September 2018

If you can't do the impossible, do the honorable

Often times when I sleep, I forget that there are painful and sometimes nearly impossible things I must eventually overcome or suffer what is to come. For a moment there I feel fine, but even in my sleep, I know that when I wake up, I will forever remember what's ahead. This is the state of my being, my every waking moment short of a few most intense minutes of the year. Every time I awaken from my amnesia, the memories hit me hard. It never goes away, and almost nothing affects it.

As time is running out, my faith in a future where I'm free from that fire is slowly fading away. Time itself has shown me, things I derive value from don't appear to be going my way. This is cause my true goals have always had poor prospects within my expected lifetime, but also because I've always had extremely hard time finding comparable value in anything else. The universe may hold unimaginable wonders, but that doesn't mean I wouldn't be stuck right where I am - until the end of time. Yet I go on.