Amiga Machine Code Letter X - More CLI

Amiga Machine Code - Letter X

This is the forth part, of a multi-part series about the system libraries. We are still looking at Letter X of the Amiga Machine Code course, and as always, make sure to read the letter, since I won’t go through all the details.

In this post we continue our exploration of the CLI from The previous post. We are still using the DOS library to interact with the command line interface of the AmigaOS.

CLI Input

When the CLI runs a program, it sets up two file handles, one for input and one for output. These two file handles are available to the program through the DOS library methods Input and Output.

Usually the input and output file handles refers to the CLI terminal, unless they were redirected by “>” and “<”, in which case they will refer to something else 😉.

Too keep my sanity, I will not repeat the documentation here, but just mention, that you can click on the links, and if you need the offsets, then look here or in the book Mapping the Amiga. 👍

We are going to look at the program mc1007, which can be found on Letter X. In the listing below, you can see the code with my comments. When running the program from the CLI, it will wait for user input via the CLI terminal. The input ends when enter is pressed, and is then echoed it back into the CLI terminal, with the prefix “Hello, “.

bsr	opendos           ; go to opendos() to open the DOS library

moveq	#40,d0            ; move 40 into d0
lea.l	input,a0          ; move input address into a0
bsr	readchar          ; go to subroutine d0 = readchar(a0, d0)

addq.l	#7,d0             ; add 7 to actual length read by readchar, where 7 is for "Hallo, "
lea.l	output,a0         ; put output address into a0
bsr	writechar         ; go to subroutine d0 = writechar(a0, d0)
rts                       ; exit program

output:
dc.b	"Hallo, "          ; fill some bytes with charecters

input:
blk.b	40,0               ; reserves 40 bytes for input
even                       ; pseudo-op for Seka. Makes the current address even by sometimes inserting a fill byte


readchar:                  ; subroutine (d0=actualLength) = readchar(a0=input,d0=length)
movem.l	d1-d7/a0-a6,-(a7)  ; push register values onto the stack
move.l	a0,a5              ; move a0 into a5 (input)
move.l	d0,d5              ; move d0 into d5 (length)
lea.l	txt_dosbase,a0     ; move txt_dosbase address into a0
move.l	(a0),a6            ; move base pointer to DOS library into a6
jsr	-54(a6)            ; call (d0=file) = Input() in DOS library
move.l	d0,d1              ; move d0 (file) into d1
move.l	a5,d2              ; move a5 (input) into d2
move.l	d5,d3              ; move d5 (length) into d3
jsr	-42(a6)            ; call (d0=actualLength) = Read(d1=file,d2=buffer,d3=length)
movem.l	(a7)+,d1-d7/a0-a6  ; pop values from the stack into the registers
rts                        ; return from subroutine

writechar:                 ; subroutine (d0=returnedLength) = writechar(a0=buffer, d0=length)
movem.l	d1-d7/a0-a6,-(a7)  ; push register values onto the stack
move.l	a0,a5              ; move a0 into a5 (buffer)
move.l	d0,d5              ; move d0 into d5 (length)
lea.l	txt_dosbase,a0     ; move txt_dosbase address into a0
move.l	(a0),a6            ; move base pointer to DOS library into a6
jsr	-60(a6)            ; call (d0=file) = Output()
move.l	d0,d1              ; move d0 (file) into d1
move.l	a5,d2              ; move a5 (buffer) into d2
move.l	d5,d3              ; move d5 (length) into d3
jsr	-48(a6)            ; call (d0=returnedLength) = Write(d1=file,d2=buffer,d3=length) in DOS library
movem.l	(a7)+,d1-d7/a0-a6  ; pop values from the stack into the registers
rts                        ; return from subroutine

opendos:                   ; opens the dos library. opendos()
movem.l	d0-d7/a0-a6,-(a7)  ; push register values onto the stack
clr.l	d0
move.l	$4,a6
lea.l	txt_dosname,a1
jsr	-408(a6)           ; call exec.library method d0 = OpenLibrary(a1,d0)
lea.l	txt_dosbase,a1
move.l	d0,(a1)
movem.l	(a7)+,d0-d7/a0-a6  ; pop values from the stack into the registers
rts                        ; return from subroutine

txt_dosname:
dc.b	"dos.library",0    ; library name terminated by zero
txt_dosbase:
dc.l	$0                 ; allocation for holding the base address of dos.library

It’s important to run this program from the CLI, otherwise it won’t work. To compile the program and make an executable, write the following in Seka.

SEKA>a
OPTIONS>
No Errors
SEKA>W
FILENAME>mc1007

Then go into the CLI and run the program and see it echo back the arguments

1.amigahd:DISK1/BREV10>mc1007
how are you doing!
Hello, how are you doing!
1.amigahd:DISK1/BREV10>

As also mentioned in the documentation, with exclamation marks, we should not close the file handles from Input and Output because they are owned by the CLI environment, which will close them, when the program terminates.

However, we should be a good citizen and close the DOS library, but that is not done in mc1007, with the result that the AmigaOS won’t be able to reclaim the memory. 😐

Memory handling in the AmigaOS

The AmigaOS is a microkernel architecture, where the kernel is called the Executive, or simply the Exec library. Its principal architect was Carl Sassenrath, who Commodore gave free hands to design a new operating system for the Amiga. He says the following about his role:

Introduced multitasking to the world of personal computers in 1985 with the Amiga Operating System Executive.

The microkernel could then load other libraries as needed, and unload them when not needed, thus preserving the limited memory.

One of the implications of the limited hardware, is that the AmigaOS has no memory protection. Every program can freely write to any memory address it wants.

In the previous post, we showed how freeing the wrong memory, could crash the whole system! 💥.

Motorola later added an MMU to the 68K family, called the 68030. At that point, the AmigaOS could have enforced memory protection on systems with the 68030, and left it out on the older platforms, since I doubt the machines were fast enough to do it in software.

Sadly, I don’t have time to actually check up on AmigaOS, to see if it eventually got memory protection. The last AmigaOS for the old Amiga 500 hardware was AmigaOS 3.9.

The book The Future Was Here, writes that many programmers, at the time, was used to program according to the three ones: One user, one program, and one computer. They came from a world of coding to the bare metal. For them, the multitasking in AmigaOS, was something they had to learn, and many got it wrong, with the result that the Amiga would crash. An Amiga user learned to save their work early and often. 😒

I think, that some of this hackish attitude shines through in the Machine Code Course, since they do not stress the point of freeing ressources, like closing the DOS library, when it’s not needed anymore.

However, this relaxed enforcment of system integrity in the AmigaOS, also made it possible for hackers to do some really impressive stuff, that pushed the Amiga hardware to it’s limits.

The book The Future Was Here goes into much more detail about the origins of the AmigaOS in chapter 6. The author Jimmy Maher, describes himself as a digital antiquarian, and that is really a good description. He has dug up a lot of war stories, that makes for an intersting read. His blog is available for free, but be warned, countless hours can be spend there 😃. If you like what you see, consider supporting him on Patreon.

In the next post, we are going to take a look at reading and writting to the disk. Stay tuned 🚀


Amiga Machine Code Course

Previous post: Amiga Machine Code Letter X - CLI.

Next post: Amiga Machine Code Letter X - Trackdisk.

Mark Wrobel
Mark Wrobel
Team Lead, developer and mortgage expert