Danger Computer

The danger computer

Piano.a86

posted by: Gar
filed under: code history

My super brilliant coworker Philip showed us a neat javascript sound generator he was playing with (source here) and it reminded me of one of the very first assembler programs I ever wrote. I hunted down the source code, and here it is.

.radix 16
org 100
        jmp short start

intro   db  0d
        db  'Piano! by GeorgeCo.'
        db  1a
notes   dw  0eee, 0e18, 0d4e, 0c8e, 0bda, 0b30, 0a8e, 09f8
        dw  0968, 08ed, 0862, 07e8, 0777, 070c, 06a7, 0647

start:  in      al,61
        and     al,0fc
        out     61,al
        xor     ax,ax
        int     16
        cmp     al,1b
        jz      done
        in      al,60
        and     al,0f
        shl     al,1
        cbw
        mov     bx,ax
        mov     ax,notes+[bx]
        out     42,al
        mov     al,ah
        out     42,al
        in      al,61
        or      al,03
        out     61,al
        mov     ah,86
        mov     cx,0001
        mov     dx,6000
        int     15
        jmp short start
done:
        ret

I'd love to walk you through this.

First here's a quote that I'm sure was the inspiration for this app, though I can't remember for sure. It's from my copy of the MASM bible for MASM 5.1

The IBM PC's speaker can be used to generate a tone by programming it via the 8255 chip at port address 61h and using the system timer (Intel 8254 chip) to control the speaker. You first set up the timer as an oscillator by sending the data byte B6h to the port 43h. Then you have to compute the ratio of the frequency of sound you want and the frequency of the timer's clock (1.19 MHz). This value is written to port 42h. The 8255 chip is then told to drive the speaker under the control of the timer. You can do this by reading port 61h and writing the value back with the first two bits set to 1 (performing a bitwise-OR with 3). This gets the sound going. Now you must wait as long as you want the sound to continue and then shut the speaker off by reading port 61h again and setting bots 0 and 0 to zero.

As you will see, this is almost 100% the path I took. I never ended up sending byte B6h to port 43h. I don't know why.

Here we go...

.radix 16
org 100

Assembler boilerplate, setting the .radix, or default base of bare numbers, to 16 (hexadecimal). Also sets the starting point of the app at 100 (hex, which is 256 decimal). This was the default for .com files. As such, numbers here are assumed to be hex unless specified otherwise (i.e. int 16 is really 0x16h aka 22 decimal)

jmp short start

intro   db  0d
        db  'Piano! by GeorgeCo.'
        db  1a

For some reason I was really into making it so if you were to type (DOS equivalent of cat) my program out it would show you what it was in plain English without any weird character codes. My .com files always started in this way, with a jump around a newline (which would move the cursor back to position 0, overwriting the bytecode for the jmp) and then a quick string describing the app (and sometimes usage info) followed by the EOF character 1a, which would cause type to stop output of the file.

GeorgeCo was what I was going to call my software company which obviously never became a real thing.

notes   dw  0eee, 0e18, 0d4e, 0c8e, 0bda, 0b30, 0a8e, 09f8
        dw  0968, 08ed, 0862, 07e8, 0777, 070c, 06a7, 0647

These were the precalculated values for the 16 notes (two octaves) I wanted to play. They were based on the a440 pitch standard. As we already know, the numbers were the ratio of the sound you wanted and the frequency of the timer's clock (1.19 MHz).

start:  in      al,61
        and     al,0fc
        out     61,al

Port 61 was the PPI (Programmable Peripheral Interface) port B on PC/XT systems. Bits 1 and 0 were the gate and data controls for the PC speaker (the little speaker inside the tower that usually beeped when you turned it on). Setting these both to 0 ensured that the PC speaker was off.

        xor     ax,ax
        int     16

Interrupt 16 was for accessing keyboard services. Calling it with ah set to 00 called the 'read (wait for) next keystroke' service. Once the interrupt returned al contained an ascii character. If al was 00, ah contained an extended ascii keystroke. In this app, the interrupt was being used for its blocking functionality only, A subsequent in call was made to get the scan code directly from the keyboard once the interrupt returned.

        cmp     al,1b
        jz      done

If the key that was pressed was escape, jump to the end of the program (and exit)

        in      al,60
        and     al,0f
        shl     al,1
        cbw

Port 60 was the PPI port A which was the most recent scan code from they keyboard hardware interrupt. It was then bitmasked with 0f and doubled (because it will be a pointer to an array of words not bytes), using cbw to expand al into a signed word in ax. Since al could never be less than 10 I have no idea why I didn't just clear ah out instead of using cbw. I'm sure it was a weird optimization thing I've forgotten.

        mov     bx,ax
        mov     ax,notes+[bx]

Here we see ax being used as an offset pointer to the array of numbers in notes. The scancode we read from the keyboard has now been used to lookup and load one of 16 numbers from that array.

        out     42,al
        mov     al,ah
        out     42,al

To set the frequency of sound you want, you wrote to port 42.

        in      al,61
        or      al,03
        out     61,al

Turn the speaker back on, now at our frequency selected via the keyboard scan code.

        mov     ah,86
        mov     cx,0001
        mov     dx,6000
        int     15

This was the BIOS wait function. It went into an interrupt-enabled waiting period of cx,dx milliseconds. This was a nice short 'blip' duration I ended up with.

        jmp short start

Do it all over again, go back and wait for a keyboard input.

Well I hope you liked this little trip down memory lane. Thanks for reading.

Your Code Isn't the Most Important Part of Your Project

posted by: Gar
filed under: code

I wrote a blog post over on the &yet blog about documentation. Go check it out.

Workout Logging

posted by: Gar
filed under: lifting

I like to log my workouts in more than one place, and they all take different formats. One takes bbcode, the next markdown. Typing them up even once in any of those formats is a chore, let alone more than one.

I wrote a little utility site to solve this problem. Now I can just type my lifts in a way that feels natural to me, without any markup at all. The site automatically translates it to the formats I need.

I also preemptively added a page that will try to convert a workout copied from fitocracy since that feels like something other people may also find useful.

For now it only does weight x reps or just reps. Time and/or distance based activities aren't supported (yet).

If you want to check it out go to lift.gar.zone

If you wanna ask for new features or you find a bug or even if you want to pitch in a contribution the site is on github.

Gar's lifts: Week of 9/28/2014

posted by: Gar
filed under: lifting

Here are this week's lifts. Yes this blog is probably going to be my workout log for awhile.

PRed on Squats this week (yay) at 305, and plan on trying 315 or more the next week. Also went for 145 on OHP and didn't make it. Finally, went for 4 plates on deadlift just to make sure I still could, will go for heavy singles next week to see where I'm at.

Sunday 9/28

OHP

60x5, 70x5, 85x3 110x5, 125x3, 135x3 110x7, 110x5, 110x6

Seated DB Press

35x12, 35x12, 30x12, 30x12

DB Side Raise

20x12x4

Cable Reverse Flye

25x12x4

EZ Bar Curl

45x12, 45x12, 40x12, 40x12

Preacher Curl

35x10, 35x10, 35x10, 35x7

Upright Row

55x10x4

Palms Down Wrist Bench Curl

15x12, 10x15, 10x12, 10x1

Palms Up Wrist Bench Curl

10x12, 15x12, 15x15, 15x12

Monday 9/29

Squat

115x5, 140x5, 170x5 215x5, 240x3, 270x3 295x1, 305x1 215x6, 215x5, 215x4

Medicine Ball Twist Crunch

(6 lb ball) 10x4

Front Squat

145x5x4

Wednesday 10/1

Bench

75x5, 95x5, 110x5 135x3, 150x3, 165x4 135x9, 135x7, 135x6

Dips

6, 5, 7, 4

Incline Bench

75x12, 75x12, 75x12, 65x12

DB Flye

30x12, 30x12, 30x10, 25x12

Incline DB Flye

25x12, 20x12, 20x12, 20x12, 20x12

Pushdown

65x20x4

Friday 10/3

Deadlift

160x5, 205x5 245x5 305x5 245x3, 385x3, 405x1 315x6, 315x5, 315x4

Weighted Hyperextension

25x12, 25x10, 10x12, 10x10

DB Row

45x12, 45x12, 45x12, 45x10

Good Morning

75x10x4

Pulldown

140x10x4

ISO Row

35x10x4

Gar's lifts: Week of 9/21/2014

posted by: Gar
filed under: lifting

Here are this week's lifts. Last week I wasn't able to do what I felt were the full max effort reps on bench, so I redid week 1 on that. The rest I did week 2. I will be spending 4 weeks this cycle, trying to get all the main lifts back up to snuff.

Sunday 9/21

OHP

60x5, 70x5, 85x3 100x3, 115x3, 130x4 100x8, 100x6, 100x5

Seated DB Press

30x12, 30x12, 35x12, 35x12

DB Side Raise

15x12x4

Cable Reverse Flye

20x12x4

EZ Bar Curl

40x12x4

Preacher Curl

30x12, 30x10x3

Upright Row

50x10x4

Palms Down Wrist Bench Curl

10x10, 10x12, 10x12

Palms Up Wrist Bench Curl

15x12, 15x12, 15x9

Monday 9/22

Squat

115x5, 140x5, 170x3 200x3, 225x3, 255x4 200x6, 200x6, 200x6

Medicine Ball Twist Crunch

(6 lb ball) 10, 10, 10, 8

Front Squat

135x5x5

Wednesday 9/24

Bench

75x5, 95x5, 110x4 130x5, 150x5, 160x6 135x9, 135x6, 135x5

Dips

4, 3, 3, 2

Incline Bench

75x12, 75x12, 65x12, 65x12

DB Flye

30x11, 25x12, 25x12, 25x12

Incline DB Flye

20x12x4

Pushdown

60x20x4

Friday 9/26

Deadlift

160x5, 200x5, 245x3 285x3, 325x3, 365x4 285x7, 285x6, 285x6

Weighted hyperextensions

10x10x4

DB Rows

40x12x4

Good morning

65x10x4

Pulldown

120x10x4

ISO Row

35x10x2