Paolo Amoroso's Journal

Assembly

I ported to Intel 8080 Assembly the Z80-MBC2 Z80 code of the led blink demo, assembled it with the Suite8080 assembler, and uploaded the HEX binary to the Z80-MBC2. Instead of printing a message to the console and blinking the User led, the program made the led turn on and the board reset, booting up the default operating system.

I have no tools to debug software that runs on the bare metal. So the next step is to investigate these clues, starting from a comparison of the Z80 and 8080 sources.

#Assembly #z80mbc2 #Suite8080

Discuss... | Reply by email...

I'm looking for source code of MS-DOS .COM programs in real-mode 8086 Assembly written in NASM syntax.

Why such weird requirements? I'm learning Assembly programming under MS-DOS and MikeOS, and cross-developing on Linux with NASM. To avoid the complexity of x86 segmentation, I prefer to focus on single-segment programs as I plan to write small applications anyway. So I'd like to study examples of how these programs organize and reference data and code.

Despite the long history and influence of MS-DOS, finding .COM code is proving unexpectedly hard.

Googling doesn't turn up much. I solicited recommendations on comp.os.ms.dos.programmer, comp.lang.asm.x86, the NASM forum, and Mastodon but, aside from a lot of engagement and interest (and the occasional suggestion I change my mind and target Windows), I got few resources.

#Assembly #MSDOS #retrocomputing #x86

Discuss... | Reply by email...

Working on Suite8080 and using the Z80MBC2 encouraged me to learn Intel 8080 and Z80 Assembly.

Now I'm dipping my toes into x86 Assembly, which I've always been curious about and attracted by. An additional motivation is discovering MikeOS, a lovely real-mode operating system in x86 Assembly that's surprisingly easy to read and understand.

Therefore, I put together an environment to cross-develop and run x86 code.

Requirements

My desktop daily driver is an ASUS Chromebox 3, with the Crostini Linux container of chromeOS that lets me run most Linux software. Any emulation and development tools must therefore be available for Linux.

Intel x86 Assembly is close enough to 8080 Assembly that x86 code looks familiar. To leverage this similarity and avoid the complexity of x86 segmentation, for the time being I want to develop 16-bit real-mode programs that fit into the single segment of a .COM executable.

The obvious choice for an operating system to run these programs is MS-DOS. Why? Because its rich environment strikes the right balance between complexity and functionality.

So the emulator must run a full MS-DOS environment and boot from media images. Which enables coding for the bare metal, a PC with no operating system.

As for the Assembly tools, I prefer to cross-develop on Linux. Although I'm a retrocmputing enthusiast, for writing and building code I want the convenience of a modern system.

MS-DOS emulation

QEMU would have been my first choice for x86 emulation but has compatibility issues with Crostini, as virtualizing in a virtual environment is tricky.

A bit of research led me to DOSBox-X, which runs fine on Crostini as it emulates rather than virtualize. DOSBox-X is a fork of DOSBox with a more fleshed out MS-DOS environment. Plus it can boot from media images.

DOSBox-X running under chromeOS on my Chromebox looks like this:

DOSBox-X session under chromeOS on an ASUS Chromebox 3.

Installing DOSBox-X on the Chromebox was straightforward. However, the Flatpak package carried almost a gigabyte worth of dependencies that nearly filled the Crostini file system. I had to resize the container from 7.5 to 10 GB to accommodate all the files and leave enough free space.

A feature I like is the emulator can mount a directory tree of the Linux file system as an MS-DOS disk. This is handy for quickly transferring cross-assembled binaries to MS-DOS.

DOSBox-X has rendering issues on chromOS, i.e. the menu bar and items flicker randomly. A workaround is to turn off the menu bar.

I tested DOSBox-X booting by checking out MikeOS, which runs well.

Assembly development

There's a vast assortment of x86 assemblers but I skipped the research by picking NASM for Linux.

I like this popular tool because it's rich of features and comes with extensive documentation. It also has a clean, non verbose syntax and can generate MS-DOS .COM programs.

To test the full cross-development and emulation toolchain I used NASM to assemble an x86 real-mode hello world demo, copied it to a directory DOSBox-X mounts as a disk, and ran it under MS-DOS. Success! A simple and low friction workflow.

#Assembly #MSDOS #chromeOS #retrocomputing #x86

Discuss... | Reply by email...

I develop programs for the Z80-MBC2 in Intel 8080 Assembly with the Suite8080 8080 assembler. Aside from the binary compatibility, the Z80-MBC2 is a Z80 system, so I wondered what Assembly mnemonics and register set the CP/M development tools work with.

It turns out my favorite CP/M debugger, the SID symbolic debugger, comes in two versions specialized on the CPU, SID and ZSID.

SID accepts as input and outputs 8080 mnemonics and registers, ZSID Z80 mnemonics and registers. For example, in this CP/M 3.0 SID session on the Z80-MBC2 I loaded my Twirl 8080 program and disassembled it with SID's l (list) command:

A>sid f:twirl.com
CP/M 3 SID - Version 3.0
NEXT MSZE  PC  END
0180 0180 0100 D4FF
#l
  0100  MVI  C,09
  0102  LXI  D,0139
  0105  CALL 0005
  0108  LXI  H,0142
  010B  MVI  B,08
  010D  PUSH H
  010E  PUSH B
  010F  MVI  C,09
  0111  LXI  D,013E
  0114  CALL 0005
  0117  POP  B7

The output contains 8080 mnemonics. An analogous ZSID session running the same disassembly command outputs Z80 mnemonics:

A>zsid f:twirl.com
SID VERS 1.4
NEXT  PC  END
0180 0100 CDFF
#l
  0100  LD   C,09
  0102  LD   DE,0139
  0105  CALL 0005
  0108  LD   HL,0142
  010B  LD   B,08
  010D  PUSH HL
  010E  PUSH BC
  010F  LD   C,09
  0111  LD   DE,013E
  0114  CALL 0005
  0117  POP  BC

SID thus lets me work in a 8080 environment on a Z80 system.

#z80mbc2 #Suite8080 #CPM #Assembly

Discuss... | Reply by email...

I've started writing a new Suite8080 demo in Intel 8080 Assembly, a CP/M program to move an ASCII character across the screen with vi's cursor keys h, j, k, and l. It's my first interactive CP/M program and does raw non-blocking I/O to read key presses.

#Assembly #Suite8080

Discuss... | Reply by email...

I ported to CP/M Twirl, a twirling bar animation demo in Intel 8080 Assembly. I originally developed it for emuStudio, an emulator and development environment that recreates a number of classic computers and CPUs. The port runs on any CP/M system with an ANSI terminal or display.

This video shows Twirl running under CP/M 3.0 on the Z80-MBC2 computer, controlled from a Minicom terminal emulator session under Crostini Linux on my Chromebox.

How it works

The demo displays an ASCII animation of a bar twirling clockwise at the home position of the console, i.e. the top left corner. After clearing the screen, Twirl repeatedly moves the cursor to the console home, prints the current animation frame, and checks for key presses to decide whether to terminate.

Although visually simple, the program demonstrates some key Assembly programming techniques such as looping, calling BDOS functions, and doing output with ANSI escape codes.

Porting

The original Twirl runs on a virtual Altair 8800 computer with a Lear Siegler ADM-3A terminal emulated by emuStudio.

However, I designed the program intending to port it to CP/M. The necessary work consisted in replacing the Altair-specific I/O rutines with equivalent BDOS calls for printing a single character (function 02h) and a string (09h) of ANSI escape codes, and checking the console status for key presses (0bh).

The program logic works as is, but I had to modify the CP/M version for defining the appropriate constants and ANSI escape strings, as well as adapting to the execution environment of the operating system and returning control to it.

Building and running

For me, the whole point of writing programs like Twirl and getting Z80 hardware is to use Suite8080, the suite of Intel 8080 Assembly cross-development tools I'm writing in Python. The CP/M port is a new Suite8080 Assembly demo I built from source on Linux with asm80, the Suite8080 assembler:

$ asm80 twirl.asm

I tested the resulting 74 bytes twirl.com executable under CP/M 2.2 with the CP/M software emulators ANSI CP/M emulator and z80pack.

Running the demo at the CP/M prompt is straightoward:

B>twirl

Pressing any key quits Twirl and returns control to the operating system.

To exeute Twirl on actual hardware I converted twirl.com to Intel HEX format with z80pack's bin2hex tool, then transferred the twirl.hex file to the Z80-MBC2 under CP/M 3.0.

It was really rewarding to see Twirl come to life on a real Z80 computer and get a sense of its performance.

The code

Here is the full code of the CP/M port of Twirl:

; Twirling bar animation.
;
; Runs on CP/M with an ANSI terminal. Press any key to quit the program.


TPA        equ      100h
BDOS       equ      0005h
WRITESTR   equ      09h                ; Write string
WRITECHR   equ      02h                ; Write character
CONSTAT    equ      0bh                ; Console status

FRAMES     equ      8                  ; Number of animation frames


           org      TPA
            
           mvi      c, WRITESTR
           lxi      d, cls             ; Clear screen
           call     BDOS

loop:      lxi      h, anim            ; Initialize frame pointer...
           mvi      b, FRAMES          ; ...and count

loop1:     push     h
           push     b
           mvi      c, WRITESTR
           lxi      d, home            ; Go to screen home
           call     BDOS
           pop      b
           pop      h

           push     h
           push     b
           mvi      c, WRITECHR
           mov      e, m               ; Print current frame
           call     BDOS
           pop      b
           pop      h

           push     h
           push     b
           mvi      c, CONSTAT         ; Get console status
           call     BDOS
           pop      b
           pop      h
           ora      a                  ; Key pressed?
           jnz      exit               ; Yes

           inx      h                  ; Point to next frame
           dcr      b                  ; One fewer frame
           jnz      loop1

           jmp      loop

exit:      ret


cls:       db       1bh, '[2J$'        ; ANSI clear screen: ESC [ 2 J
home:      db       1bh, '[H$'         ; ANSI go to screen home: ESC [ H
anim:      db       '|/-\|/-\'         ; 8 frames

           end

The program represents the animation as the string |/-\|/-\, a sequence of ASCII characters that show the bar at various steps of the rotation.

After clearing the screen, Twirl begins the main loop loop: by initializing the frame pointer and count. An inner loop loop1: prints each animation frame, i.e. the characters of the animation sequence.

The inner loop moves the cursor to the home position and prints the current frame. Then it updates the frame pointer and count, and checks whether a key was pressed. In case of a key press the inner loop exits the program, otherwise it jumps back to the beginning of the main loop.

#Assembly #retrocomputing #Suite8080 #z80mbc2 #CPM

Discuss... | Reply by email...

I'm about to resume working on my Suite8080 project after putting it aside for a few months. And I bought a Z80 homebrew computer.

To refresh my 8080 Assembly to work on the Suite8080 code, and my Z80 to play with the new computer, I'm rereading the book Z-80 and 8080 Assembly Language Programming by Kathe Spracklen (Hayden Books, 1979).

It's a short, clear resource that includes several worked out exercises. It presents and compares the code samples and concepts in both instruction sets. The book also covers how to implement basic data structures in Assembly, which makes it stand out.

#Assembly #intel8080 #z80 #books

Discuss... | Reply by email...

Encouraged by the emuStudio developer, I donated to the project an Intel 8080 Assembly demo. It's a modified version of Twirl, my program to display a twirling bar animation.

Here's the code I contributed:

; Twirling bar animation by Paolo Amoroso <info@paoloamoroso.com>
;
; Runs on an Altair 8800 with an ADM-3A terminal. Press any key
; to interrupt the program.


FRAMES    equ  8              ; Number of animation frames

CLS       equ  1ah            ; ADM-3A escape sequence
HOME      equ  1eh            ; ADM-3A escape sequence
STATUS    equ  10h            ; Input status port
READY     equ  1              ; Character ready status mask


          mvi  a, CLS         ; Clear screen
          call putchar

loop:     lxi  h, ANIM        ; Initialize frame pointer...
          mvi  b, FRAMES      ; ...and count

loop1:    mvi  a, HOME        ; Go to home
          call putchar

          mov  a, m           ; Print current frame
          call putchar

          push psw
          in   STATUS
          ani  READY          ; Key pressed?
          jnz  exit           ; Yes
          pop  psw

          inx  h
          dcr  b
          jnz  loop1

          jmp  loop


exit:     pop psw             ; Clear psw left on stack
          hlt


ANIM:     db   '|/-\|/-\'     ; 8 frames


include	'include\putchar.inc'

Unlike the original code, which I designed to run on emuStudio and be easy to port to CP/M, this new version is intended to run only on emuStudio. This was an occasion to simplify the code.

The main difference is the escape codes for VT100-compatible terminals on CP/M are strings, whereas the ADM-3A codes Twirl sends on emuStudio are single bytes. This allows the program to print the codes with the putchar library subroutine instead of putstr. Doing away with putstr no longer requires saving and restoring the HL register pair in the calling sequences.

Finally, I added code to terminate the program if a key is pressed.

#intel8080 #Assembly

Discuss... | Reply by email...

I wrote Twirl, an Intel 8080 Assembly program that displays the ASCII animation of a bar twirling at the home position of the terminal screen. To make things a bit more clear I recorded a screencast to show the running program and what the animation looks like.

Motivation

I developed this short program as an Assembly learning exercise related to Suite8080, a suite of Intel 8080 Assembly cross-development tools in Python I'm working on. The project, which is a lot of fun, drove me down a rabbit hole of Intel 8080 Assembly, CP/M, and retrocomputing.

I additionally wanted to explore the emuStudio execution environment and get the algorithm working. Now that it does, I can port Twirl to CP/M and add the program to Suite8080 as an Assembly demo.

The execution environment

Twirl runs on a virtual Altair 8800 computer in emuStudio, a beautiful emulator and Assembly development environment that can recreate a number of classic computers and CPUs.

I designed Twirl to run on a bare Altair 8800 connected to a Lear Siegler ADM-3A terminal. Any amount of RAM will do as the assembled code is only a few dozen bytes long. In the execution environment there's no host operating system or resident monitor, just a few basic character I/O subroutines emuStudio ships with.

Once started, Twirl runs an infinite animation loop. To terminate it I click the emuStudio Stop emulation button.

By default the Altair 8800 runs at 2 Mhz in emuStudio, which causes some terminal flicker when executing Twirl. That's why I usually set the CPU frequency to 4 MHz, closer to that of late CP/M computers.

The code

Here's the source code of Twirl:

; twirl.asm
; Twirling bar animation
;
; Runs in emuStudio emulating an Altair 8800 with an
; ADM-3A terminal


FRAMES          equ     8               ; Number of animation frames


                push    h
                lxi     h, CLS
                call    putstr          ; Clear screen
                pop     h

loop:           lxi     h, ANIM
                mvi     b, FRAMES

loop1:          push    h
                lxi     h, HOME
                call    putstr          ; Go to home
                pop     h

                mov     a, m
                push    h
                call    putchar         ; Print current frame
                pop     h

                inx     h
                dcr     b
                jnz     loop1

                jmp     loop

                hlt


CLS:            db      1ah, 0          ; ADM-3A escape sequence
HOME:           db      1eh, 0          ; ADM-3A escape sequence
ANIM:           db      '|/-\|/-\'      ; 8 frames


include 'include\putstr.inc'
include 'include\putchar.inc'

After clearing the screen, the program starts an infinite loop that repeats going through each frame of the animation, moving the cursor to the home position, and printing the current frame. The animation consists of a byte array containing the sequence of characters — frames — to display.

Some data definitions follow, i.e. the ADM-3A escape sequences to clear the screen and go to home, as well as the animation array.

The program ends with include directives for importing the following I/O subroutines:

  • putstr: prints to the terminal the 0-terminated string pointed to by the HL register pair
  • putchar: prints to the terminal the character in register A

The calls are enclosed in code that saves and restores the registers the subroutines overwrite.

Future work

What does it take to port Twirl to CP/M?

I use z80pack as my main CP/M emulator, so I design the Suite8080 demos to run on a machine with a VT100 or compatible terminal like the CP/M systems z80pack emulates.

I'll begin by converting the terminal escape codes from ADM-3A to VT100. Next, I'll replace the subroutine calls to equivalent BDOS calls. Finally, I want to support terminating the program by pressing any key. This will require code to check the console status for key presses.

#intel8080 #Assembly

Discuss... | Reply by email...