• Please review our updated Terms and Rules here

Looking for Sample 8080 Assembly or Machine language listings for simple programs ...

Code:
                          00001 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                          00002 ;
                          00003 ; Writes a "zero" into all the RAM from 0C00 to 0FFF.
                          00004 ;
                          00005 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                          00006
 0005                     00007 BDOS    equ     5                       ; CP/M function entry point
                          00008
 0100                     00009         org     100h                    ; It runs in the TPA
                          00010
 0100                     00011 Start:
 0100 21 0C00        [10] 00012         lxi     H,0C00h                 ; Point HL to 0C00h
 0103 11 0400        [10] 00013         lxi     D,0FFFh-0C00h+1         ; Load number of bytes
                          00014
 0106                     00015 Loop:
 0106 36 00          [10] 00016         mvi     M,0                     ; Store a zero at [HL]
                          00017
 0108 23              [5] 00018         inx     H                       ; Point HL to next location
                          00019
 0109 1B              [5] 00020         dcx     D                       ; Decrement count
                          00021
 010A 7A              [5] 00022         mov     A,D                     ; Has count reached 0?
 010B B3              [4] 00023         ora     E
 010C C2 0106        [10] 00024         jnz     Loop                    ; No
                          00025
 010F 0E 00           [7] 00026         mvi     C,0                     ; Exit to CP/M
 0111 CD 0005        [17] 00027         call    BDOS
                          00028
 0100                     00029         end     Start

Hi,

Yeah, under CP/M the program needs to start at 0100 so to exit back to CP/M the program should check for an exit character like an "ESC" or something else.

I did try the program, but I can't see any zeros starting at address C00h or 140 0000Q ...

.
 
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Starting from address 0C00 every time I hit the <ENTER> key,
;   let me type in a byte that will be saved in that RAM address
;   and then step to the next until it gets to address 0FFF.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BDOS    equ    5            ; CP/M function entry point

CR    equ    0Dh            ; Carriage return in ASCII
LF    equ    0Ah            ; Line feed in ASCII

    org    100h            ; It runs in the TPA

Start:
    lxi    H,0C00h            ; Point HL to 0C00h
    lxi    D,0FFFh-0C00h+1        ; Load number of bytes

ByteLoop:
    call    Prompt            ; Prompt for a value

    call    GetHexByte        ; Get the value
    jc    ByteLoop        ; Try again if error

    mov    M,A            ; Store the value at [HL]

    inx    H            ; Point HL to next location

    dcx    D            ; Decrement count

    mov    A,D            ; Has count reached 0?
    ora    E
    jnz    ByteLoop        ; No

    call    PutCRLF

    mvi    C,0            ; Exit to CP/M
    call    BDOS

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Issue a prompt for a value in the form of <address>:
;
; Input:
;   register HL contains the address
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Prompt:
    call    PutCRLF            ; Start a new line

    call    PutHexAddress        ; Convert and output address

    mvi    A,':'            ; Output ': '
    call    PutChar
    mvi    A,' '
    call    PutChar

    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Output the contents of register HL in hexadecimal
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PutHexAddress:
    mov    A,H            ; Get high byte of address

    call    PutHexByte        ; Convert and output it

    mov    A,L            ; Get low byte of address

; Fall through to PutHexByte

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Output the contents of register A in hexadecimal
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PutHexByte:
    push    PSW            ; Save value

    rrc                ; Isolate the upper nybble
    rrc
    rrc
    rrc
    call    PutHexNybble        ; Convert to ASCII and output

    pop    PSW            ; Recover value

; Fall through to PutHexNybble

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Output the low byte of register A in hexadecimal
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PutHexNybble:
    ani    0Fh            ; Isolate the lower nybble

    cpi    10            ; Below 10 is numeric
    jc    Numeric

    adi    'A'-10            ; Convert 10..15 to 'A'..'F'

    jmp    Common

Numeric:
    adi    '0'            ; Convert 0..9 to '0'..'9'

Common:

; Fall through to PutChar

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Output the character in register A to the console
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PutChar:
    push    PSW            ; Save all registers
    push    B
    push    D
    push    H

    mov    E,A            ; Put the character where CP/M wants
    mvi    C,2            ; Output it
    call    BDOS

    pop    H            ; Recover all registers
    pop    D
    pop    B
    pop    PSW

    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Output carriage return and line feed to the console
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PutCRLF:
    mvi    A,CR            ; Output a carriage return
    call    PutChar
    mvi    A,LF            ; Output a line feed
    call    PutChar

    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Input a byte in hexadecimal from the console to register A
;
; Output:
;   carry flag set if error
;   carry flag clear if valid value in register A
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GetHexByte:
    mvi    B,'0'            ; Presume leading zero

    call    GetChar            ; Get input
    call    PutChar            ; Echo it

    cpi    0Dh            ; No value entered?
    jz    GotNoValue

    mov    C,A            ; Remember the value

    call    GetChar            ; Get more input
    call    PutChar            ; Echo it

    cpi    0Dh            ; Only one hexit entered?
    jz    DoConversion

    mov    B,C            ; Previous is now the leading hexit
    mov    C,A            ; This is the lower one

ExpectCRLoop:
    call    GetChar            ; Need to get a carriage return
    call    PutChar            ; Echo it

    cpi    0Dh            ; If CR, then try to convert it
    jz    DoConversion

    mvi    B,0            ; Bad input, spoil it

    jmp    ExpectCRLoop

DoConversion:
    mov    A,B            ; Get leading hexit

    call    ConvertHexNybble    ; Try converting to binary
    jc    GotBadValue

    rlc                ; Shift into upper nybble
    rlc
    rlc
    rlc

    mov    B,A            ; Save upper nybble

    mov    A,C            ; Get low hexit

    call    ConvertHexNybble    ; Try converting to binary
    jc    GotBadValue

    ora    B            ; Combine and clear carry flag

    ret

GotNoValue:
    push    D
    push    H

    lxi    D,NoValue

    jmp    ErrorMsg

GotBadValue:
    push    D
    push    H

    lxi    D,BadValue

ErrorMsg:
    mvi    C,9            ; Output string
    call    BDOS

    lxi    D,Entered
    mvi    C,9
    call    BDOS

    pop    H
    pop    D

    stc

    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Convert a nybble in hexadecimal in register A
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
ConvertHexNybble:
    cpi    '0'            ; Bad if < '0'
    jc    BadConvert

    cpi    '9'+1            ; Numeral if <= '9'
    jc    ConvertNumeral

    ani    0DFh            ; Fold lowercase to uppercase

    cpi    'A'            ; Bad if < 'A'
    jc    BadConvert

    cpi    'F'+1            ; Bad if > 'F'
    jnc    BadConvert

    sui    'A'-10            ; Convert 'A'..'F' to 10..15

    ret

ConvertNumeral:
    sui    '0'            ; Convert '0'..'9' to 0..9

    ret

BadConvert:
    stc                ; Set carry flag to return error

    ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Input a character from the console to register A
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
GetChar:
    push    B            ; Save all registers
    push    D
    push    H

    mvi    C,1            ; Input it
    call    BDOS

    pop    H            ; Recover all registers
    pop    D
    pop    B

    ret

BadValue:
    db    'Bad$'

NoValue:
    db    'No$'

Entered:
    db    ' value entered$'

    end    Start

Hi,

This program showed me two of each character I type on my console screen, although it may have been putting the characters into RAM starting at 0C00h ... I didn't see anything on my DAZZLER II screen which would have been really neat. That said, I think since the DAZZLER II S-100 board has its own onboard RAM, perhaps simply writing to the Altair 8800c RAM isn't enough.

That's kind of a bummer! I think it would be neat to be able to type characters on the computer console and see the associated color or black & white dots appear on the DAZZLER II display.


.
 
Hi,

This program showed me two of each character I type on my console screen, although it may have been putting the characters into RAM starting at 0C00h ... I didn't see anything on my DAZZLER II screen which would have been really neat. That said, I think since the DAZZLER II S-100 board has its own onboard RAM, perhaps simply writing to the Altair 8800c RAM isn't enough.

That's kind of a bummer! I think it would be neat to be able to type characters on the computer console and see the associated color or black & white dots appear on the DAZZLER II display.


.
Me bad. I was testing this on an 8080 emulator, not actually under CP/M. I forgot that CP/M automatically echoes the input.

Is there an emulator which also emulates the Dazzler?
 
If you go with the Cookbook, remember that the first edition used mnemonics for the 8080 that were invented by Scelbi, and different from the standard Intel mnemonics. This was changed in the second edition - so be sure to pick up the second edition Cookbook with the standard mnemonics.

smp
Scelbi did not make up the mnemonics.

The 8080 evolved from the Intel 8008. Those were 8008 mnemonics. The 8080 is a superset of the 8008 and Intel later redefined the mnemonics.

On most processors, ADD is an instruction to perform addition.

Unless you are on an Intel 8008 where it is specialized to add the D register to the accumulator. The 8008 has a strange assembly language:

SUB - subtract the B register from the accumulator
SUM - subtract the contents of memory whose address is in registers H and L from the accumulator
 
This almost sounds like some homework assignment, except what school uses 8080 anymore ;)
Most schools that I know of use x86, 68000, MIPS, ARM or possibly RISCV to teach assembly language.

8085 trainers are still being made, so I have to presume those are being used somewhere. The 8085 is essentially an 8080 software-wise.
 
3. A game “Zombie Escape” … the idea that the game would be a first person, shooter game. The user would see simple black and white graphics 128x128 display on the screen similar to this image:

View attachment 1284538


Using the same "JIKL" keys to turn left, go forward, go back, turn right ... IF the key is held down, the person is running, SPACEBAR = Shoot a Stun Gun at a Zombie that pops into view so you can either run past or turn and enter a new hallway (maybe it's a portal to a different room on the map) and finally one portal would be labeled "EXIT" to do just like "Q" ... Quit back to the CP/M prompt. IF scoring is added, it could be the number of rooms visited + stuns.
I played a similar game called RATRUN on a SuperPET at the university computer center.

Puzzles like





would be really dazzling on a Dazzler...
 
Me bad. I was testing this on an 8080 emulator, not actually under CP/M. I forgot that CP/M automatically echoes the input.

Is there an emulator which also emulates the Dazzler?

Hi,

I want to do this all on my Altair 8800c computer ... TDL Z80 ZPU, 64K RAM, CP/M 2.2b, MBASIC v5.x and DAZZLER II board.

As for a simulator I do have the AltairZ80 Emulator on my PC ... I used it to move the Cromemco DOS Graphics for DAZZLER to the CP/M .dsk ...

I've attached the AltairZ80 Emulator I use on my PC ... UnZip the files into a folder, run "AltairZ80.exe" then to get into CP/M run "do cpmDAZ.ini" and you'll see the DAZZLER screen pop up.


We also need more support on the S100Google Group to show there is a lot of interest in the DAZZLER boards:



.
 

Attachments

  • 20240825-BackUps.zip
    3.8 MB · Views: 1
I played a similar game called RATRUN on a SuperPET at the university computer center.

Puzzles like





would be really dazzling on a Dazzler...

Hi,

That's the idea! We need more people with easy MBASIC programming skills to start creating their ideas for the DAZZLER boards.

There is no reason for the late 1970s to die. It's a fun world to play in.


.
 
This is why I resist most of the processor emulators. They usually do not have a way to easily connect different I/O. I suppose Mime has such documentation.
I usually write my own emulator when needed.
I/O is the trickiest part. Just executing instructions is a small part of the problem. I/O can have a number of issues. One of the ones most commonly ignored is time and processor cycles.
Dwight
 
Hi,

Okay, my brain is having trouble putting all the pieces together to learn when they're in a larger program ... I'm thinking what I need is a hand full of tiny assembly routines to play with and see if I can learn this.

Just the tiniest program to load a character from a keyboard input and store it to an address.

Then another tiny program to load a character from a memory address and move it to another memory address.

Then another tiny program to load a character from a memory address and send it out the console terminal.

Then another tiny program to load an 8-bit number from a memory address and then another 8-bit number from a different address and add them together to save at yet a third memory address.
Then another tiny program to load an 8-bit number from a memory address and then another 8-bit number from a different address and divide the first from the second and save at yet a third memory address.



.
 
Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Writes a "zero" into all the RAM from 0C00 to 0FFF.
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

BDOS    equ     5                       ; CP/M function entry point

        org     100h                    ; It runs in the TPA

Start:
        lxi     H,1C00h                 ; Point HL to 0C00h
                                        ; Note that although the instruction
                                        ; says "H", it means HL. This is
                                        ; common in 8080 assembly.
        lxi     D,0FFFh-0C00h+1         ; Load number of bytes
                                        ; This is calculating the number of
                                        ; bytes using the constant expression
                                        ; within the assembler. The CPU
                                        ; does not perform this calculation
                                        ; at runtime.
                                        ; Also note, that the D here means
                                        ; DE register pair. The result here
                                        ; is a 16 bit number. In this case,
                                        ; 0x400, or 1024 bytes.

Loop:
        mvi     M,0                     ; Store a zero at [HL]
                                        ; It says M here, but means "where
                                        ; HL is pointing". So, since HL
                                        ; was initially set to 0x1C00 above,
                                        ; the first 0 will be stored in the
                                        ; address 0x1C00

        inx     H                       ; Point HL to next location
                                        ; Again, H mean HL. Since HL is a 16
                                        ; bit register, INX will bump it by
                                        ; 1 and handle the 16 bath for us.
                                        ; The first time through, HL will
                                        ; be incremented to 0x1C01.

        dcx     D                       ; Decrement count
                                        ; Similarly, this is decrementing the
                                        ; DE register pair.

        mov     A,D                     ; Has count reached 0?
        ora     E
                                        ; This is a bit of idiomatic code 
                                        ; here, and it's not intutive at its
                                        ; face from the simple reading of the
                                        ; mnemonics.
                                        ; What is happening here is that we
                                        ; have the DE register pair, which 
                                        ; contains the count. The MOV copies
                                        ; the 8-bit D register into the 
                                        ; accumulator. Unlike above where D
                                        ; or H mean DE and HL, respectively,
                                        ; here D means D.
                                        ; Next, it does an OR of the A
                                        ; with the other 8-bit register E
                                        ; ORing two registers is quick way
                                        ; to tell if they're BOTH zero.
                                        ; 1 or 0 is 1, 0 or 1 is 1. The only
                                        ; way you get zero is 0 or 0.
                                        ; You'll note that since it's ORing
                                        ; E into A, the result is in A.
                                        ; Anytime the A register is set to 
                                        ; zero, the Z flag is set, otherwise
                                        ; it is reset.
                                        ; So, if D and E are both 0, which 
                                        ; the combination DE register pair is
                                        ; 0, then that means we have consumed
                                        ; all of the count that was stored in 
                                        ; DE and now we can exit the loop.
        jnz     Loop                    ; No
                                        ; DE is not 0, so Z flag is reset, so
                                        ; we jump to the Loop label.
                                        ; Otherwise, DE is zero, thus A is
                                        ; zero, thus the Z flag is set, and
                                        ; we continue past to end the routine.

                                        ; And we're done, back to CP/M using
                                        ; BDOS
        mvi     C,0                     ; Exit to CP/M
        call    BDOS

        end     Start

I updated the previous routine with more detailed comments.

When reading these, you must have a description of the instruction set handy to explain each instruction.

And, FYI, unless you're using a factor of 2, multiplying and dividing are long processes. That's intermediate level assembly language to understand how they work, frankly. You'd be surprised how far you can get shifting and adding to multiply by constants.
 
an alternate version of the above with a few differences that you should become familiar with

Code:
; fill memory from 0C00-0FFF with zero
    ORG 0100H ; start of Transient Program Area where CP/M programs run
start:
    LXI H,0C00H ; start address of memory block
    MVI C,0 ; value to fill memory with (if it needs to be changeable)
loop:
    MOV M,C ; store value at HL (or just use MVI M,0 or whatever)
    INX H ; increment HL pointer

    MOV A,H ; get H
    CPI 10H ; = 1000H / 256, compare the high byte of HL against the address of the first byte after the memory block
    JNZ loop ; could also use JNC
    ; don't check L because the end is aligned 256
    ; but can also use MOV A,L, CPI, JNZ/JNC if the low byte of the address needs to be checked too

    MVI C,0 ; exit to Command Control Processor
    CALL 5 ; BDOS

    END start

This was all done off the cuff, I'd be surprised if it works as-is, but you don't just need to know one way to do it, you need to know a lot of ways to do it. For instance you could add an INR C to store successive values into the memory block.

It's also why using a disassembler is a great way to learn all these variations, because you get to see so many of them.
 
Back
Top