home *** CD-ROM | disk | FTP | other *** search
- version equ 1
- ;History:577,1
-
- include defs.asm ;SEE ENCLOSED COPYRIGHT MESSAGE
-
- ; PC/FTP Packet Driver Source, conforming to version 1.08 of spec
- ; Krishnan Gopalan and Gregg Stefancik, Clemson Univesity Engineering
- ; Computer Operations.
- ; Date: September 1, 1989
- ; Portions of the code have been adapted from the 3c505 driver for NCSA
- ; Telnet by Bruce Orchard and later modified by Warren Van Houten and krus
- ; @diku.dk.
-
- ; Permission is granted to any individual or institution to use,copy,
- ; modify or redistribute this software provided this notice is retained.
- ; The authors make no guarantee to the suitability of the software for
- ; any purpose. Any damage caused by using this program is the responsibility
- ; of the user and not the authors.
-
- code segment word public
- assume cs:code, ds:code
-
- ; 3c505 card definitions
- ;port addresses
- ECOMMAND equ 0
- ESTATUS equ 2
- EDATA equ 4
- ECONTROL equ 6
-
- ;ECONTROL bit definitions
- EN_ATTN equ 0200q
- EN_FLSH_DATA equ 0100q
- EN_DMA_ENABLE equ 0040q
- EN_TO_HOST equ 0020q
- EN_TERMINAL_CNT_ENBLE equ 0010q
- EN_COMMAND_ENABLE equ 0004q
- EN_HSF2 equ 0002q
- EN_HSF1 equ 0001q
-
- ;ESTATUS bit definitions
-
- EN_DATA_READY equ 0200q
- EN_HOST_COMMAND_EMPTY equ 0100q
- EN_ADAPTER_COMMAND_FULL equ 0040q
- EN_TO_HOST equ 0020q
- EN_DMA_DONE equ 0010q
- EN_ASF3 equ 0004q
- EN_ASF2 equ 0002q
- EN_ASF1 equ 0001q
- ; auxiliary dma register bit definition
- EN_BURST equ 0001q
-
- ; timeout values
- SECOND EQU 36
- RESDEL EQU 6
- RESTO EQU 15*SECOND
- CMDBTO EQU 6
- CMDCTO EQU 6
- RETRYDELAY EQU 6
- RCMDTO EQU 6
- RESPTO EQU 6
-
- recvbuf db 4096 dup(?) ; size of receive buffer
- rbufct dw ? ; recv buffer count
-
- pcblen dw ? ; length of pcb
- pcbaddr dw ? ; address of pcb
-
- lastcon db ? ; last control to board
- cmdlen dw ? ; length of command
-
- cbsh equ 50
- cbs equ cbsh*2
-
- icmdb db cbs dup(?)
- icmd db cbsh dup(?)
-
- fconc db 0 ; flag configure 82586
- fgeth db 0 ; flag:get ethernet address
- fseth db 0 ; flag:set ethernet address
- fxmit db 0 ; flag:transmit packet
-
- cconc db 02h ; command configure 82586
- db 2 ; 2 more bytes
- dw 1 ; receive broadcasts
-
- rconc db 2 dup(?);Response; configure
- rconc_st dw ? ; status
-
- cgeth db 03h ; command get ethernet address
- db 00 ; no more bytes
-
- txmit db 09h ; command ; transmit packet
- db 06 ; 6 more bytes
- tx_offset dw ? ; offset of host transmit buffer
- tx_segment dw ? ; segment of host transmit buffer
- tx_length dw ? ; packet length
-
- rxmit db 2 dup(?); Response tx packet
- rx_offset dw ? ; buffer offset
- rx_segment dw ? ; buffer segment
- rx_status dw ? ; completion status
- rx_cstatus dw ? ; 82586 status
-
- rgeth db 2 dup(?); Response get Ethernet address
- rgeth_ad db 6 dup(?); -- address
-
- cseth db 10h ; command :set station address
- db 6 ; 6 more bytes
- cseth_ad db 6 dup(?); ethernet address
-
- rseth db 2 dup(?); response set ethernet address
- rseth_status dw ? ; status
-
- crecv db 08h ; command receive
- db 08 ; 8 more bytes
- crecv_offset dw ? ; buffer offset
- crecv_segment dw ? ; buffer segment
- crecv_length dw ? ; buffer length
- crecv_timeout dw ? ; timeout
-
- rr db 2 dup(?); Response; receive
- rr_offset dw ? ; buffer offset
- rr_segment dw ? ; buffer segment
- rr_dmalen dw ? ; buffer dmalen
- rr_length dw ? ; packet length
- rr_status dw ? ; completion status
- rr_rstatus dw ? ; 82586 receive status
- rr_time dd ? ; time tag
-
- public int_no,io_addr
-
- int_no db 2,0,0,0 ;must be four bytes long for get_number.
- io_addr dw 0300h,0 ; io addr for card(jumpers)
-
- public driver_class, driver_type, driver_name, driver_function, parameter_list
-
- driver_class db BLUEBOOK, IEEE8023, 0 ;from the packet spec
- driver_type db 2 ;from the packet spec
- driver_name db '3C505',0 ;name of the driver.
- driver_function db 2
- parameter_list label byte
- db 1 ;major rev of packet driver
- db 9 ;minor rev of packet driver
- db 14 ;length of parameter list
- db EADDR_LEN ;length of MAC-layer address
- dw GIANT ;MTU, including MAC headers
- dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
- dw 0 ;(# of back-to-back MTU rcvs) - 1
- dw 0 ;(# of successive xmits) - 1
- int_num dw 0 ;Interrupt # to hook for post-EOI
- ;processing, 0 == none,
-
- public rcv_modes
- rcv_modes dw 4 ;number of receive modes in our table.
- dw 0,0,0,rcv_mode_3
-
- include movemem.asm
-
- public bad_command_intercept
- bad_command_intercept:
- ;called with ah=command, unknown to the skeleton.
- ;exit with nc if okay, cy, dh=error if not.
- mov dh,BAD_COMMAND
- stc
- ret
-
- public as_send_pkt
- ; The Asynchronous Transmit Packet routine.
- ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
- ; interrupts possibly enabled.
- ; Exit with nc if ok, or else cy if error, dh set to error number.
- ; es:di and interrupt enable flag preserved on exit.
- as_send_pkt:
- ret
-
- public drop_pkt
- ; Drop a packet from the queue.
- ; Enter with es:di -> iocb.
- drop_pkt:
- assume ds:nothing
- ret
-
- public xmit
- ; Process a transmit interrupt with the least possible latency to achieve
- ; back-to-back packet transmissions.
- ; May only use ax and dx.
- xmit:
- assume ds:nothing
- ret
-
-
- send_pkt_toobig:
- mov dh,NO_SPACE
- stc
- ret
- public send_pkt
- send_pkt:
- ;enter with es:di->upcall routine, (0:0) if no upcall is desired.
- ; (only if the high-performance bit is set in driver_function)
- ;enter with ds:si -> packet, cx = packet length.
- ;exit with nc if ok, or else cy if error, dh set to error number.
- assume ds:nothing
-
- cmp cx,GIANT ; Is this packet too large?
- ja send_pkt_toobig
-
- push si ; save si for lodsw
- push ds ; save ds
- push es ; save es for timer
- movseg ds,cs
-
- mov tx_segment,ds ;tx->data
- mov tx_offset,si ;tx_offset ->segment offset
-
- mov ax,cx ; save cx
- cmp ax,RUNT ; compare with minimum limit
- ja pkt_ok ; go if ok
- mov ax,RUNT ; make length minimum allowed
- pkt_ok:
- mov tx_length,ax ; put in request
-
- mov fxmit,0 ; clear response received flag
-
- mov si, offset txmit ; si->request(pcb)
- mov ax,8 ;length of pcb
-
- call outpcb ; send command to board
-
- mov cx,tx_length ; transfer data
- inc cx ; round up to a count of words.
- shr cx,1
-
- pop es
- pop ds
- pop si
-
- loadport
- setport EDATA
- tx_1:
- lodsw
- out dx,ax ; output word
- setport ESTATUS
- tx_2:
- in al,dx ; get status
- test al, EN_DATA_READY ; ready for next word ?
- jz tx_2 ;no
-
- setport EDATA
- loop tx_1 ; loop thru buffer
-
- sti ;interrupts have to be on to modify fxmit.
- tx_3:
- test fxmit,0ffh ; is transmit over
- jz tx_3 ; no
- ret
-
-
- public set_address
-
- set_address:
- ;enter with ds:si -> Ethernet address, CX = length of address.
- ;exit with nc if okay, or cy, dh=error if any errors.
- assume ds:nothing
-
- cmp cx, EADDR_LEN ; check if address ok
- je set_addr_1
- mov dh,BAD_ADDRESS ; don't like length
- stc
- ret
- set_addr_1:
- mov di,offset cseth_ad ;di->destination offset
- rep movsb ; return address
- mov si,offset cseth ; si->request ethernet address
- mov ax,8 ; request length -> ax
- mov fseth,0 ; clear response received flag
- call outpcb ; send the pcb
-
- mov ax,RESTO ; get response time
- call set_timeout
- set_addr_2:
- test fseth,0ffh ; pcb answered?
- jnz set_addr_3
- call do_timeout ; has time expired
- jne set_addr_2 ; no, go back
- stc ; error
- ret
- set_addr_3:
- mov cx,EADDR_LEN ;return their address length.
- clc
- ret
-
- rcv_mode_3:
- ;receive mode 3 is the only one we support, so we don't have to do anything.
- ret
-
-
- public set_multicast_list
- set_multicast_list:
- ;enter with ds:si ->list of multicast addresses, ax = number of addresses,
- ; cx = number of bytes.
- ;return nc if we set all of them, or cy,dh=error if we didn't.
- mov dh,NO_MULTICAST
- stc
- ret
-
-
- public terminate
- terminate:
- ret
-
- public reset_interface
- reset_interface:
- ;reset the interface.
- assume ds:code
-
- mov al, EN_ATTN OR EN_FLSH_DATA ; hard reset command
- loadport
- setport ECONTROL
- out dx,al ; do reset
- mov ax,RESDEL ; get reset delay
- call set_timeout
- rst1:
- call do_timeout
- jne rst1 ; wait for reset
- mov al,EN_COMMAND_ENABLE ; command interrupt enable
- mov lastcon,al ; save last command
- out dx,al ; release reset
- mov ax,RESDEL
- call set_timeout
- rst2:
- call do_timeout
- jne rst2 ; wait to start reset
- mov ax,RESTO ; add time out
- call set_timeout
- rst3:
- call getstat ; getstatus
- and ax,EN_ASF1 OR EN_ASF2 ;
- cmp ax,EN_ASF1 OR EN_ASF2 ; both on ?
- jne resdone
- call do_timeout ; long enough?
- jne rst3 ; no
- ret
- resdone:
- ret
- ;called when we want to determine what to do with a received packet.
- ;enter with cx = packet length, es:di -> packet type, dl = packet class.
- extrn recv_find: near
-
- ;called after we have copied the packet into the buffer.
- ;enter with ds:si ->the packet, cx = length of the packet.
- extrn recv_copy: near
-
- ;call this routine to schedule a subroutine that gets run after the
- ;recv_isr. This is done by stuffing routine's address in place
- ;of the recv_isr iret's address. This routine should push the flags when it
- ;is entered, and should jump to recv_exiting_exit to leave.
- ;enter with ax = address of routine to run.
- extrn schedule_exiting: near
-
- ;recv_exiting jumps here to exit, after pushing the flags.
- extrn recv_exiting_exit: near
-
- extrn count_in_err: near
- extrn count_out_err: near
-
- public recv
- recv:
- ;called from the recv isr. All registers have been saved, and ds=cs.
- ; all interrupts come here
- ;Upon exit, the interrupt will be acknowledged.
- assume ds:code
- ;sti
- cld
- recv1:
- loadport
- setport ESTATUS
- in al,dx ; read flags
- and al,EN_ADAPTER_COMMAND_FULL ;
- jnz recv2 ; yes,full
- jmp interrupt_done
- ;there might be a response, clear flags to check for response
- recv2:
- mov ax,RCMDTO ; incoming command time out
- call set_timeout
-
- mov al,lastcon ; last control->ax
- and al,NOT (EN_HSF1 OR EN_HSF2) ; clear flags
- mov lastcon,al
-
- loadport
- setport ECONTROL
- out dx,al ; clear flags in cntl reg
-
- mov di,offset icmdb ; di->incoming command buffer
-
- recv3:
- loadport
- setport ESTATUS
- in al,dx ; status->al
-
- mov cx,ax ; status->cx
- test al,EN_ADAPTER_COMMAND_FULL ; is adapter register full
- jnz recv4 ; yes go ahead to read from
- call do_timeout ; is time up ?
- jne recv3 ; give some more time
- jmp recv1 ; give up
- recv4:
- loadport
- setport ECOMMAND
- in al,dx ; get command byte
- and cl,EN_ASF1 OR EN_ASF2
- cmp cl,EN_ASF1 OR EN_ASF2 ; both on?
- je recv5 ; end of command
- mov [di],al ;save byte
- inc di ;inc di
- mov ax,di ; current pointer->ax
- sub ax,offset icmdb ; - start of buffer
- cmp ax,cbs ; full?
- jl recv3
-
- mov si,(offset icmdb)+cbsh ; si->middle of buffer
- mov di,offset icmdb ; di->start of buffer
- mov cx,cbsh ; size of half buffer->cx
- movseg es,ds
- rep movsb
-
- jmp recv3 ; loop for more bytes
- recv5:
- movseg es,ds
- mov ah,0
- mov cmdlen,ax ; save cmdlen
- mov si,di
- sub si,cmdlen
- mov di,offset icmd
- mov cx,cmdlen
-
- rep movsb ; copy
-
- mov al,icmd ; check
-
- cmp al,32h ; Configure?
- je respconfig
-
- cmp al,33h ; get ethernet address?
- je respgetaddr ; yes
-
- cmp al,39h ; transmit complete?
- je transmit ; yes
-
- cmp al,40h ; set ethernet address
- je respsetaddr ; yes
-
- cmp al,38h ; receive complete?
- je resprecv
-
- jmp recv1
-
- respconfig:
-
- movseg es,ds
- mov si,offset icmd
- mov di,offset rconc
- mov cx,2
-
- rep movsw
- mov fconc,1
- jmp recv1
-
- respgetaddr:
- movseg es,ds
-
- mov si,offset icmd
- mov di,offset rgeth
- repmov 8
-
- mov fgeth,1
- jmp recv1
-
- respsetaddr:
-
- mov si,offset icmd ; si->command received
- mov di,offset cseth ; di->set ethernet address resp
- movseg es,ds
- repmov 4
-
- mov fseth,1
- jmp recv1
-
- transmit:
- mov si,offset icmd ; si->command received
- mov di,offset rxmit ; response,transmit packet
- movseg es,ds
- repmov 10 ; response length->cx
-
- mov fxmit,1 ; response received
- jmp recv1 ; return from interrupt
-
- resprecv:
- mov si,offset icmd ; si->command received
- mov di,offset rr ; di->receive response
- movseg es,ds
- mov cx,9 ; response length->cx
-
- rep movsw ; move response
- mov di,offset recvbuf ; di->receive buffer
- mov cx,rr_length ; cx=message length
- inc cx ; round up
- shr cx,1 ; convert to words
-
- mov al,lastcon ; lastcontrol->al
- or al,EN_TO_HOST OR EN_HSF1 ; set direction & ack
- mov lastcon,al ; response
-
- loadport
- setport ECONTROL
- out dx,al ; pass direction
-
- setport ESTATUS
-
- resprecv_1:
-
- in al,dx ; get status
- test al,EN_DATA_READY ; data ready ?
- jz resprecv_1
-
- setport EDATA ; data word->ax
- in ax,dx ; get word
- stosw ; store word in buffer
-
- setport ESTATUS
- loop resprecv_1 ; loop if more words
-
- mov al,lastcon
- and al,NOT (EN_TO_HOST OR EN_HSF1)
- mov lastcon,al
-
- loadport ; dx->control register
- setport ECONTROL
- out dx,al
-
- mov cx,rr_length ; cx->packet length
-
- mov di,offset recvbuf+EADDR_LEN+EADDR_LEN
- ; reset di to beginning
- ; point to type field
- ; of buffer
-
- mov dl, BLUEBOOK ;assume bluebook Ethernet.
- mov ax, es:[di]
- xchg ah, al
- cmp ax, 1500
- ja BlueBookPacket
- inc di ;set di to 802.2 header
- inc di
- mov dl, IEEE8023
- BlueBookPacket:
-
- call recv_find ; first call
-
- mov ax,es ; ax->es
- or ax,di ; is es=di=0?
- je rcv_no_copy
-
- mov cx,rr_length ; cx->packet length
- push es
- push di
-
- mov si,offset recvbuf ; prepare to copy the packet
- call movemem
-
- pop si
- pop ds
- assume ds:nothing
- mov cx,rr_length
-
- call recv_copy ; second call
- movseg ds,cs
- rcv_no_copy:
- call anotherrecv ; start another recv
- jmp recv1
-
- interrupt_done:
- ret
-
- ;*****************************OUTPCB***********************************
- ;outpcb send pcb to board, retry until accepted
- ; entry ax = number of bytes in pcb
- ; si = address of pcb
- outpcb proc near
- mov pcblen,ax ; save pcb length
- mov pcbaddr,si ; save pcb address
- pcb_0:
- mov cx,pcblen ;length->cx
- mov si,pcbaddr ; address->si
- cli ; stop interrupts
- mov al,lastcon ; save last command
- and al, NOT (EN_HSF1 OR EN_HSF2); clear flags
- mov lastcon,al
- sti ; enable interrupts
- loadport
- setport ECONTROL
- out dx,al ; send control
- setport ECOMMAND
- pcb_1:
- lodsb
- out dx,al ; send command byte
- mov ax,CMDBTO ; get time out
- call set_timeout
- chk_hcre:
- call getstat
- and al, EN_HOST_COMMAND_EMPTY ; command accepted
- jne pcb_2 ; go on
- call do_timeout
- jne chk_hcre ; time is still left
- jmp cmdretry ; retry command
- pcb_2:
- loop pcb_1 ; loop
- loadport
- setport ECONTROL
- cli ; disable interrupts
- mov al,lastcon ; last control -> al
- or al,(EN_HSF1 OR EN_HSF2) ; set end of command
- mov lastcon,al ; save last control
- out dx,al ; send flag bits
- setport ECOMMAND
- mov ax,pcblen
- out dx,al ; send pcb length
- sti ; enable interrupts
- mov ax,CMDCTO ; time out for acceptance
- call set_timeout
- pcb_3:
- call getstat
- and al,(EN_ASF1 OR EN_ASF2) ; just keep status flags
- cmp al,1 ; accepted
- je cmdaccept
- cmp al,2
- je cmdretry
- call do_timeout
- jne pcb_3
-
- cmdretry:
- mov ax,RETRYDELAY ;add retry delay
- call set_timeout
- pcb_4:
- call do_timeout
- jne pcb_4
- jmp pcb_0
-
- cmdaccept:
- cli
- mov al,lastcon
- and al, NOT (EN_HSF1 OR EN_HSF2) ; turn off end of command flag
- mov lastcon,al ; save last control
- sti ; enable interrupts
- loadport
- setport ECONTROL
- out dx,al ; pass control byte
- ret
- outpcb endp
- ;*************get status of board***********************
- getstat proc near
- push bx
- push dx
- loadport
- setport ESTATUS
-
- stat_1:
- in al,dx ; get status into al
- mov bl,al ; save al
- in al,dx ; get status again
- cmp al,bl ; same status ?
- jne stat_1
- pop dx
- pop bx
- ret
- getstat endp
-
- ; UPDATE BUFFER POINTERS AND ISSUE ANOTHER RECV
- ; update recv
- anotherrecv proc near
- mov ax,rbufct
- mov crecv_offset,ax
- inc ax
- mov rbufct,ax
- mov ax,10
- mov si,offset crecv
-
- call outpcb
- ret
- anotherrecv endp
-
- include timeout.asm
-
- public timer_isr
- timer_isr:
- ;if the first instruction is an iret, then the timer is not hooked
- iret
-
- ;any code after this will not be kept after initialization. Buffers
- ;used by the program, if any, are allocated from the memory between
- ;end_resident and end_free_mem.
- public end_resident,end_free_mem
- end_resident label byte
- end_free_mem label byte
-
- public usage_msg
- usage_msg db "usage: 3C505 [options] <packet_int_no> <hardware_irq> <io_addr>",CR,LF,'$'
- public copyright_msg
- copyright_msg db "Packet Driver for 3c505, version ",'0'+(majver / 10),'0'+(majver mod 10), ".",CR,LF
- db "Portions copyright 1989, Krishnan Gopalan & Gregg Stefancik.",CR,LF
- db "Clemson University Engineering Comp Ops.",CR,LF,'$'
- no_board_msg:
- db "3c505 apparently not present at this address.",CR,LF,'$'
- int_no_name db "Interrupt number ",'$'
- io_addr_name db "I/O port ",'$'
-
- extrn set_recv_isr: near
-
- ;enter with si -> argument string, di -> word to store.
- ;if there is no number, don't change the number.
- extrn get_number: near
-
- ;enter with dx -> name of word, di -> dword to print.
- extrn print_number: near
-
- ;-> the assigned Ethernet address of the card.
- extrn rom_address: byte
-
- public parse_args
- parse_args:
- ;exit with nc if all went well, cy otherwise.
- mov di,offset int_no
- call get_number
- mov di,offset io_addr
- call get_number
- clc
- ret
-
- public etopen
- etopen:
- ;if all is okay,
- ; reset the 3c505 board takes about 15-20 seconds
- loadport
- setport ECONTROL
- mov al,EN_ATTN OR EN_FLSH_DATA ; reset command
- out dx,al ; do reset
- if 0
- mov ax,1
- call set_timeout
- reset1:
- call do_timeout
- jne reset1 ; wait for reset to propagate
- else
- endif
-
- mov al,EN_COMMAND_ENABLE ; command interrupt enable
- mov lastcon,al ; save last command
- out dx,al ; release reset
- mov ax,RESDEL ; time to wait
- call set_timeout
- reset2:
- call do_timeout
- jne reset2
-
- mov ax,RESTO ;this is the long wait.
- call set_timeout
- reset3:
- call getstat
- and ax, EN_HSF1 OR EN_HSF2
- cmp ax,EN_HSF1 OR EN_HSF2
- jne resetdone
- call do_timeout
- jne reset3
- jmp openfail ; open failed
-
- resetdone:
- call set_recv_isr ;install the interrupt handler
-
- ;Tell the 3c505 board to receive packets
- mov si,offset cconc
- mov ax,4
- mov fconc,0
-
- call outpcb
-
- mov ax,RESTO
- call set_timeout
- open_1:
- test fconc,0ffh
- jnz setuprecv
-
- call do_timeout
- jne open_1
-
- ; set up the recv buffers
-
- setuprecv:
- mov rbufct,0 ; clear buffer counter
- mov crecv_length,1600 ; buffer length,1600
-
- mov crecv_timeout,0
-
- startrecv:
- mov ax,rbufct ; buffer counter->ax
- mov crecv_offset,ax ; buf no used as offset
- inc ax ; count buffer
- mov rbufct,ax
- mov ax,10 ; length of pcb
- mov si,offset crecv ;si->command receive
-
- call outpcb ; pass the pcb
- mov ax,rbufct
- cmp ax,10
- jl startrecv
-
- mov si,offset cgeth ; si->pcb address
- mov ax,2 ; length of pcb
- mov fgeth,0 ; clear response received flag
-
- call outpcb
-
- mov ax,RESTO ; get wait time
- call set_timeout
- get_addr_1:
- test fgeth,0ffh ; answered?
- jnz get_addr_2 ; yes
- call do_timeout
- jnz get_addr_1 ; test again
- jmp openfail ; open failed
-
- get_addr_2:
- movseg es,ds
- mov si,offset rgeth_ad ; load source of address
- mov di,offset rom_address ; point it to the canonical buffer.
- repmov EADDR_LEN ; copy address
-
- mov al, int_no ; Get board's interrupt vector
- add al, 8
- cmp al, 8+8 ; Is it a slave 8259 interrupt?
- jb set_int_num ; No.
- add al, 70h - 8 - 8 ; Map it to the real interrupt.
- set_int_num:
- xor ah, ah ; Clear high byte
- mov int_num, ax ; Set parameter_list int num.
-
- clc
- ret
-
- ;if we got an error,
- openfail:
- mov dx,offset no_board_msg
- stc
- ret
-
-
- public print_parameters
- print_parameters:
- mov di,offset int_no
- mov dx,offset int_no_name
- call print_number
- mov di,offset io_addr
- mov dx,offset io_addr_name
- call print_number
- ret
-
- code ends
- end
-